From c283a7e148cec4204972fee2de923216fbbd02b6 Mon Sep 17 00:00:00 2001 From: mamingshuai Date: Wed, 2 Jun 2021 00:05:48 +0800 Subject: [PATCH] update OpenHarmony 2.0 Canary --- .gitattributes | 15 + BUILD.gn | 24 + LICENSE | 177 ++ README.en.md | 36 - README.md | 148 +- README_zh.md | 127 + figures/en-us_image_0000001162536643.png | Bin 0 -> 79823 bytes figures/zh-cn_image_0000001162536643.png | Bin 0 -> 141053 bytes .../include/idevice_status_change_listener.h | 48 + .../distributeddatafwk/include/ikvstore.h | 100 + .../include/ikvstore_client_death_observer.h | 48 + .../include/ikvstore_data_service.h | 176 ++ .../include/ikvstore_observer.h | 53 + .../include/ikvstore_resultset.h | 101 + .../include/ikvstore_single.h | 191 ++ .../include/ikvstore_snapshot.h | 81 + .../include/ikvstore_sync_callback.h | 52 + .../distributeddatafwk/include/inner_types.h | 30 + .../distributeddatafwk/src/app_blob.cpp | 116 + .../src/app_change_notification.cpp | 60 + .../src/app_distributed_kv_data_manager.cpp | 28 + .../app_distributed_kv_data_manager_impl.cpp | 263 ++ .../app_distributed_kv_data_manager_impl.h | 65 + .../src/app_kvstore_conflict_data_impl.cpp | 76 + .../src/app_kvstore_conflict_data_impl.h | 61 + .../src/app_kvstore_impl.cpp | 562 ++++ .../distributeddatafwk/src/app_kvstore_impl.h | 189 ++ .../src/app_kvstore_result_set_impl.cpp | 148 + .../src/app_kvstore_result_set_impl.h | 80 + .../distributeddatafwk/src/blob.cpp | 211 ++ .../src/change_notification.cpp | 159 ++ .../distributeddatafwk/src/data_query.cpp | 679 +++++ .../src/delegate_mgr_callback.h | 55 + .../device_status_change_listener_client.cpp | 30 + .../device_status_change_listener_client.h | 32 + .../src/distributed_kv_data_manager.cpp | 333 +++ .../idevice_status_change_listener_impl.cpp | 76 + .../distributeddatafwk/src/ikvstore.cpp | 749 +++++ .../src/ikvstore_client_death_observer.cpp | 30 + .../src/ikvstore_data_service.cpp | 648 +++++ .../src/ikvstore_observer.cpp | 239 ++ .../src/ikvstore_resultset.cpp | 326 +++ .../src/ikvstore_single.cpp | 1525 ++++++++++ .../src/ikvstore_snapshot.cpp | 544 ++++ .../src/ikvstore_sync_callback.cpp | 89 + .../distributeddatafwk/src/kvstore_client.cpp | 267 ++ .../distributeddatafwk/src/kvstore_client.h | 72 + .../src/kvstore_client_death_observer.cpp | 33 + .../src/kvstore_client_death_observer.h | 31 + .../src/kvstore_death_recipient_impl.cpp | 45 + .../src/kvstore_death_recipient_impl.h | 38 + .../src/kvstore_observer_client.cpp | 66 + .../src/kvstore_observer_client.h | 56 + .../src/kvstore_observer_nb_impl.h | 87 + .../src/kvstore_resultset_client.cpp | 113 + .../src/kvstore_resultset_client.h | 61 + .../src/kvstore_service_death_notifier.cpp | 143 + .../src/kvstore_service_death_notifier.h | 72 + .../src/kvstore_snapshot_client.cpp | 194 ++ .../src/kvstore_snapshot_client.h | 53 + .../src/kvstore_sync_callback_client.cpp | 36 + .../src/kvstore_sync_callback_client.h | 40 + .../src/single_kvstore_client.cpp | 451 +++ .../src/single_kvstore_client.h | 100 + .../distributeddatafwk/test/BUILD.gn | 252 ++ .../test/unittest/app_blob_test.cpp | 196 ++ .../test/unittest/app_conflict_test.cpp | 224 ++ .../app_distributed_kv_data_manager_test.cpp | 426 +++ .../app_distributed_kv_store_test.cpp | 1698 +++++++++++ .../test/unittest/blob_test.cpp | 251 ++ ...stributed_kv_data_manager_encrypt_test.cpp | 136 + .../distributed_kv_data_manager_test.cpp | 846 ++++++ .../test/unittest/kvstore_client_test.cpp | 1354 +++++++++ .../unittest/kvstore_snapshot_client_test.cpp | 486 ++++ .../unittest/local_subscribe_store_test.cpp | 2346 ++++++++++++++++ .../single_kvstore_client_query_test.cpp | 609 ++++ .../unittest/single_kvstore_client_test.cpp | 1066 +++++++ .../innerkits/app_distributeddata/BUILD.gn | 99 + .../app_distributeddata/include/app_blob.h | 76 + .../include/app_change_notification.h | 64 + .../app_device_status_change_listener.h | 40 + .../include/app_distributed_kv_data_manager.h | 107 + .../app_distributeddata/include/app_kvstore.h | 167 ++ .../include/app_kvstore_conflict_data.h | 47 + .../include/app_kvstore_corruption_observer.h | 28 + .../include/app_kvstore_observer.h | 39 + .../include/app_kvstore_result_set.h | 76 + .../app_distributeddata/include/app_types.h | 173 ++ .../app_distributeddata/include/visibility.h | 27 + interfaces/innerkits/distributeddata/BUILD.gn | 87 + .../innerkits/distributeddata/include/blob.h | 111 + .../include/change_notification.h | 72 + .../distributeddata/include/data_query.h | 542 ++++ .../include/device_status_change_listener.h | 32 + .../include/distributed_kv_data_manager.h | 161 ++ .../distributeddata/include/kvstore.h | 124 + .../include/kvstore_death_recipient.h | 29 + .../include/kvstore_observer.h | 44 + .../include/kvstore_result_set.h | 71 + .../include/kvstore_snapshot.h | 94 + .../include/kvstore_sync_callback.h | 40 + .../distributeddata/include/single_kvstore.h | 255 ++ .../innerkits/distributeddata/include/types.h | 269 ++ .../distributeddata/include/visibility.h | 18 + ohos.build | 66 + .../distributeddataservice/adapter/BUILD.gn | 58 + .../distributeddataservice/adapter/LICENSE | 177 ++ .../adapter/account/BUILD.gn | 48 + .../adapter/account/src/account_delegate.cpp | 30 + .../account/src/account_delegate_impl.cpp | 121 + .../account/src/account_delegate_impl.h | 45 + .../adapter/account/test/BUILD.gn | 55 + .../account/test/account_delegate_test.cpp | 71 + .../adapter/autils/BUILD.gn | 43 + .../adapter/autils/src/constant.cpp | 126 + .../adapter/autils/src/directory_utils.cpp | 164 ++ .../adapter/autils/src/kv_scheduler.cpp | 110 + .../autils/src/thread_pool/kv_store_task.cpp | 40 + .../src/thread_pool/kv_store_thread.cpp | 62 + .../autils/src/thread_pool/kv_store_thread.h | 41 + .../src/thread_pool/kv_store_thread_pool.cpp | 33 + .../thread_pool/kv_store_thread_pool_impl.cpp | 105 + .../thread_pool/kv_store_thread_pool_impl.h | 52 + .../adapter/autils/test/BUILD.gn | 47 + .../test/unittest/concurrent_map_test.cpp | 72 + .../test/unittest/kv_scheduler_test.cpp | 109 + .../unittest/kv_store_thread_pool_test.cpp | 130 + .../adapter/broadcaster/BUILD.gn | 40 + .../broadcaster/src/broadcast_sender.cpp | 33 + .../broadcaster/src/broadcast_sender_impl.cpp | 28 + .../broadcaster/src/broadcast_sender_impl.h | 30 + .../adapter/communicator/BUILD.gn | 58 + .../communicator/src/app_device_handler.cpp | 102 + .../communicator/src/app_device_handler.h | 60 + .../communicator/src/app_pipe_handler.cpp | 85 + .../communicator/src/app_pipe_handler.h | 63 + .../adapter/communicator/src/app_pipe_mgr.cpp | 170 ++ .../adapter/communicator/src/app_pipe_mgr.h | 56 + .../src/ark_communication_provider.cpp | 66 + .../src/ark_communication_provider.h | 47 + .../src/communication_provider.cpp | 32 + .../src/communication_provider_impl.cpp | 128 + .../src/communication_provider_impl.h | 77 + .../adapter/communicator/src/data_buffer.cpp | 65 + .../adapter/communicator/src/data_buffer.h | 73 + .../src/process_communicator_impl.cpp | 191 ++ .../communicator/src/softbus_adapter.h | 107 + .../src/softbus_adapter_standard.cpp | 646 +++++ .../adapter/communicator/test/BUILD.gn | 68 + .../communication_provider_impl_test.cpp | 240 ++ .../adapter/dfx/BUILD.gn | 53 + .../adapter/dfx/src/dds_trace.cpp | 113 + .../adapter/dfx/src/dfx_code_constant.h | 32 + .../src/fault/communication_fault_impl.cpp | 26 + .../dfx/src/fault/communication_fault_impl.h | 31 + .../dfx/src/fault/database_fault_impl.cpp | 26 + .../dfx/src/fault/database_fault_impl.h | 32 + .../adapter/dfx/src/fault/fault_reporter.cpp | 21 + .../dfx/src/fault/runtime_fault_impl.cpp | 26 + .../dfx/src/fault/runtime_fault_impl.h | 30 + .../dfx/src/fault/service_fault_impl.cpp | 26 + .../dfx/src/fault/service_fault_impl.h | 31 + .../adapter/dfx/src/hiview_adapter.cpp | 265 ++ .../adapter/dfx/src/hiview_adapter.h | 104 + .../adapter/dfx/src/reporter.cpp | 83 + .../api_performance_statistic_impl.cpp | 27 + .../api_performance_statistic_impl.h | 32 + .../src/statistic/database_statistic_impl.cpp | 26 + .../src/statistic/database_statistic_impl.h | 31 + .../dfx/src/statistic/statistic_reporter.cpp | 21 + .../src/statistic/traffic_statistic_impl.cpp | 26 + .../src/statistic/traffic_statistic_impl.h | 31 + .../src/statistic/visit_statistic_impl.cpp | 26 + .../dfx/src/statistic/visit_statistic_impl.h | 31 + .../adapter/dfx/src/value_hash.h | 57 + .../adapter/dfx/test/BUILD.gn | 121 + .../unittest/distributeddata_dfx_mst_test.cpp | 79 + .../unittest/distributeddata_dfx_ut_test.cpp | 263 ++ .../test/unittest/fake/hiview/fake_hiview.cpp | 17 + .../dfx/test/unittest/fake/hiview/hievent.cpp | 16 + .../dfx/test/unittest/fake/hiview/hiview.cpp | 16 + .../fake/hiview/include/fake_hiview.h | 59 + .../unittest/fake/hiview/include/hievent.h | 190 ++ .../unittest/fake/hiview/include/hiview.h | 129 + .../include/account/account_delegate.h | 63 + .../adapter/include/account/visibility.h | 27 + .../adapter/include/autils/concurrent_map.h | 101 + .../adapter/include/autils/constant.h | 186 ++ .../adapter/include/autils/directory_utils.h | 50 + .../adapter/include/autils/kv_scheduler.h | 55 + .../adapter/include/autils/kv_store_task.h | 39 + .../include/autils/kv_store_thread_pool.h | 43 + .../include/autils/platform_specific.h | 25 + .../adapter/include/autils/serializable.h | 45 + .../adapter/include/autils/time_utils.h | 38 + .../adapter/include/autils/visibility.h | 19 + .../include/broadcaster/broadcast_sender.h | 40 + .../adapter/include/broadcaster/visibility.h | 19 + .../communicator/app_data_change_listener.h | 33 + .../communicator/communication_provider.h | 87 + .../include/communicator/idevice_query.h | 35 + .../communicator/process_communicator_impl.h | 66 + .../include/dfx/db_meta_callback_delegate.h | 41 + .../adapter/include/dfx/dds_trace.h | 52 + .../adapter/include/dfx/dfx_types.h | 142 + .../adapter/include/dfx/fault_reporter.h | 30 + .../adapter/include/dfx/reporter.h | 42 + .../adapter/include/dfx/statistic_reporter.h | 31 + .../adapter/include/dfx/visibility.h | 19 + .../adapter/include/log/log_print.h | 56 + .../adapter/include/log/visibility.h | 19 + .../include/permission/permission_validator.h | 70 + .../adapter/include/permission/visibility.h | 18 + .../include/security/security_adapter.h | 23 + .../adapter/include/utils/crypto_utils.h | 51 + .../adapter/include/utils/json_utils.h | 69 + .../adapter/include/utils/kvstore_utils.h | 43 + .../adapter/permission/BUILD.gn | 52 + .../src/client_permission_validator.cpp | 62 + .../src/client_permission_validator.h | 65 + .../permission/src/permission_validator.cpp | 91 + .../adapter/permission/test/BUILD.gn | 54 + .../unittest/permission_validator_test.cpp | 83 + .../adapter/security/BUILD.gn | 55 + .../adapter/security/src/block_integer.cpp | 39 + .../adapter/security/src/block_integer.h | 53 + .../adapter/security/src/security.cpp | 484 ++++ .../adapter/security/src/security.h | 97 + .../adapter/security/src/security_adapter.cpp | 76 + .../adapter/security/src/sensitive.cpp | 161 ++ .../adapter/security/src/sensitive.h | 52 + .../adapter/test/BUILD.gn | 27 + .../adapter/utils/BUILD.gn | 58 + .../adapter/utils/src/crypto_utils.cpp | 122 + .../adapter/utils/src/kvstore_utils.cpp | 58 + services/distributeddataservice/app/BUILD.gn | 118 + .../app/distributed_data.rc | 23 + .../app/src/backup_handler.cpp | 366 +++ .../app/src/backup_handler.h | 50 + .../app/src/device_change_listener_impl.cpp | 44 + .../app/src/device_change_listener_impl.h | 36 + .../app/src/device_kvstore_impl.cpp | 282 ++ .../app/src/device_kvstore_impl.h | 53 + .../app/src/device_kvstore_observer_impl.cpp | 89 + .../app/src/device_kvstore_observer_impl.h | 34 + .../app/src/device_kvstore_resultset_impl.cpp | 64 + .../app/src/device_kvstore_resultset_impl.h | 35 + .../app/src/flowctrl_manager/BUILD.gn | 32 + .../kvstore_flowctrl_manager.cpp | 74 + .../kvstore_flowctrl_manager.h | 61 + .../app/src/kvstore_account_observer.cpp | 31 + .../app/src/kvstore_account_observer.h | 52 + .../app/src/kvstore_app_accessor.cpp | 122 + .../app/src/kvstore_app_accessor.h | 48 + .../app/src/kvstore_app_manager.cpp | 733 +++++ .../app/src/kvstore_app_manager.h | 104 + .../app/src/kvstore_common.h | 31 + .../app/src/kvstore_data_service.cpp | 1315 +++++++++ .../app/src/kvstore_data_service.h | 186 ++ .../app/src/kvstore_impl.cpp | 800 ++++++ .../app/src/kvstore_impl.h | 135 + .../app/src/kvstore_meta_manager.cpp | 1120 ++++++++ .../app/src/kvstore_meta_manager.h | 302 ++ .../app/src/kvstore_observer_impl.cpp | 96 + .../app/src/kvstore_observer_impl.h | 47 + .../app/src/kvstore_resultset_impl.cpp | 226 ++ .../app/src/kvstore_resultset_impl.h | 70 + .../app/src/kvstore_snapshot_impl.cpp | 374 +++ .../app/src/kvstore_snapshot_impl.h | 86 + .../app/src/kvstore_sync_manager.cpp | 181 ++ .../app/src/kvstore_sync_manager.h | 82 + .../app/src/kvstore_user_manager.cpp | 185 ++ .../app/src/kvstore_user_manager.h | 66 + .../app/src/query_helper.cpp | 670 +++++ .../app/src/query_helper.h | 89 + .../app/src/single_kvstore_impl.cpp | 1500 ++++++++++ .../app/src/single_kvstore_impl.h | 135 + .../app/src/uninstaller/BUILD.gn | 50 + .../app/src/uninstaller/uninstaller.cpp | 25 + .../app/src/uninstaller/uninstaller.h | 33 + .../app/src/uninstaller/uninstaller_impl.cpp | 34 + .../app/src/uninstaller/uninstaller_impl.h | 31 + .../distributeddataservice/app/test/BUILD.gn | 404 +++ .../distributeddata_account_event_test.cpp | 583 ++++ .../app/test/unittest/kvstore_app_manager.cpp | 733 +++++ .../app/test/unittest/kvstore_backup_test.cpp | 251 ++ .../test/unittest/kvstore_data_service.cpp | 1328 +++++++++ .../unittest/kvstore_data_service_test.cpp | 63 + .../kvstore_flowctrl_manager_test.cpp | 280 ++ .../kvstore_impl_logical_isolation_test.cpp | 294 ++ .../kvstore_impl_physical_isolation_test.cpp | 321 +++ .../unittest/kvstore_sync_manager_test.cpp | 408 +++ .../app/test/unittest/uninstaller_test.cpp | 53 + .../unittest/ut_kvstore_nb_delegate_impl.cpp | 354 +++ .../unittest/ut_kvstore_nb_delegate_impl.h | 130 + .../libs/distributeddb/BUILD.gn | 220 ++ .../common/include/auto_launch.h | 138 + .../distributeddb/common/include/db_common.h | 61 + .../common/include/db_constant.h | 96 + .../distributeddb/common/include/db_errno.h | 144 + .../distributeddb/common/include/db_types.h | 125 + .../common/include/endian_convert.h | 55 + .../libs/distributeddb/common/include/hash.h | 31 + .../common/include/json_object.h | 108 + .../distributeddb/common/include/log_print.h | 56 + .../common/include/macro_utils.h | 45 + .../common/include/notification_chain.h | 143 + .../common/include/param_check_utils.h | 53 + .../distributeddb/common/include/parcel.h | 157 ++ .../common/include/performance_analysis.h | 123 + .../common/include/platform_specific.h | 67 + .../distributeddb/common/include/ref_object.h | 80 + .../common/include/res_finalizer.h | 41 + .../common/include/runtime_context.h | 113 + .../common/include/schema_object.h | 236 ++ .../common/include/schema_utils.h | 109 + .../distributeddb/common/include/semaphore.h | 48 + .../distributeddb/common/include/task_pool.h | 54 + .../common/include/value_hash_calc.h | 88 + .../common/include/value_object.h | 70 + .../distributeddb/common/include/version.h | 65 + .../distributeddb/common/src/auto_launch.cpp | 910 ++++++ .../distributeddb/common/src/db_common.cpp | 304 ++ .../distributeddb/common/src/db_constant.cpp | 58 + .../common/src/evloop/include/event_fd.h | 74 + .../common/src/evloop/include/ievent.h | 58 + .../common/src/evloop/include/ievent_loop.h | 49 + .../common/src/evloop/src/event_impl.cpp | 387 +++ .../common/src/evloop/src/event_impl.h | 76 + .../src/evloop/src/event_loop_epoll.cpp | 287 ++ .../common/src/evloop/src/event_loop_epoll.h | 57 + .../common/src/evloop/src/event_loop_impl.cpp | 594 ++++ .../common/src/evloop/src/event_loop_impl.h | 85 + .../src/evloop/src/event_loop_select.cpp | 80 + .../common/src/evloop/src/event_loop_select.h | 49 + .../common/src/evloop/src/ievent.cpp | 59 + .../common/src/evloop/src/ievent_loop.cpp | 43 + .../common/src/flatbuffer_schema.cpp | 1023 +++++++ .../libs/distributeddb/common/src/hash.cpp | 46 + .../distributeddb/common/src/json_object.cpp | 718 +++++ .../common/src/lock_status_observer.cpp | 110 + .../common/src/lock_status_observer.h | 42 + .../distributeddb/common/src/log_print.cpp | 131 + .../common/src/notification_chain.cpp | 337 +++ .../common/src/param_check_utils.cpp | 180 ++ .../libs/distributeddb/common/src/parcel.cpp | 446 +++ .../common/src/performance_analysis.cpp | 235 ++ .../common/src/platform_specific.cpp | 262 ++ .../libs/distributeddb/common/src/query.cpp | 127 + .../common/src/query_expression.cpp | 229 ++ .../distributeddb/common/src/ref_object.cpp | 198 ++ .../common/src/runtime_context.cpp | 40 + .../common/src/runtime_context_impl.cpp | 578 ++++ .../common/src/runtime_context_impl.h | 147 + .../common/src/schema_object.cpp | 1220 ++++++++ .../distributeddb/common/src/schema_utils.cpp | 477 ++++ .../distributeddb/common/src/semaphore.cpp | 61 + .../distributeddb/common/src/task_pool.cpp | 41 + .../common/src/task_pool_impl.cpp | 300 ++ .../distributeddb/common/src/task_pool_impl.h | 85 + .../distributeddb/common/src/task_queue.cpp | 78 + .../distributeddb/common/src/task_queue.h | 40 + .../common/src/time_tick_monitor.cpp | 162 ++ .../common/src/time_tick_monitor.h | 72 + .../distributeddb/common/src/types_export.cpp | 81 + .../distributeddb/common/src/value_object.cpp | 172 ++ .../communicator/include/combine_status.h | 56 + .../include/communicator_aggregator.h | 163 ++ .../include/communicator_type_define.h | 73 + .../communicator/include/frame_combiner.h | 78 + .../communicator/include/frame_retainer.h | 80 + .../communicator/include/iadapter.h | 68 + .../communicator/include/icommunicator.h | 59 + .../include/icommunicator_aggregator.h | 56 + .../communicator/include/message.h | 212 ++ .../communicator/include/message_transform.h | 45 + .../communicator/include/network_adapter.h | 88 + .../communicator/include/object_holder.h | 26 + .../include/object_holder_typed.h | 48 + .../communicator/include/parse_result.h | 172 ++ .../include/send_task_scheduler.h | 99 + .../communicator/src/combine_status.cpp | 84 + .../communicator/src/communicator.cpp | 249 ++ .../communicator/src/communicator.h | 89 + .../src/communicator_aggregator.cpp | 856 ++++++ .../communicator/src/communicator_linker.cpp | 457 +++ .../communicator/src/communicator_linker.h | 115 + .../communicator/src/frame_combiner.cpp | 256 ++ .../communicator/src/frame_header.h | 73 + .../communicator/src/frame_retainer.cpp | 244 ++ .../communicator/src/header_converter.cpp | 82 + .../communicator/src/header_converter.h | 36 + .../communicator/src/message_transform.cpp | 24 + .../communicator/src/network_adapter.cpp | 351 +++ .../communicator/src/protocol_proto.cpp | 992 +++++++ .../communicator/src/protocol_proto.h | 112 + .../communicator/src/send_task_scheduler.cpp | 285 ++ .../communicator/src/serial_buffer.cpp | 240 ++ .../communicator/src/serial_buffer.h | 68 + .../include/auto_launch_export.h | 49 + .../libs/distributeddb/include/query.h | 201 ++ .../distributeddb/include/query_expression.h | 169 ++ .../libs/distributeddb/include/types_export.h | 173 ++ .../interfaces/include/get_query_info.h | 30 + .../include/iprocess_communicator.h | 103 + .../include/iprocess_system_api_adapter.h | 56 + .../include/kv_store_changed_data.h | 39 + .../interfaces/include/kv_store_delegate.h | 108 + .../include/kv_store_delegate_manager.h | 126 + .../interfaces/include/kv_store_errno.h | 25 + .../include/kv_store_nb_conflict_data.h | 50 + .../interfaces/include/kv_store_nb_delegate.h | 201 ++ .../interfaces/include/kv_store_observer.h | 31 + .../interfaces/include/kv_store_result_set.h | 69 + .../include/kv_store_snapshot_delegate.h | 40 + .../distributeddb/interfaces/include/types.h | 91 + .../src/kv_store_changed_data_impl.cpp | 66 + .../src/kv_store_changed_data_impl.h | 50 + .../interfaces/src/kv_store_delegate_impl.cpp | 464 ++++ .../interfaces/src/kv_store_delegate_impl.h | 116 + .../src/kv_store_delegate_manager.cpp | 573 ++++ .../interfaces/src/kv_store_errno.cpp | 66 + .../src/kv_store_nb_conflict_data_impl.cpp | 70 + .../src/kv_store_nb_conflict_data_impl.h | 46 + .../src/kv_store_nb_delegate_impl.cpp | 829 ++++++ .../src/kv_store_nb_delegate_impl.h | 145 + .../src/kv_store_result_set_impl.cpp | 172 ++ .../interfaces/src/kv_store_result_set_impl.h | 82 + .../src/kv_store_snapshot_delegate_impl.cpp | 89 + .../src/kv_store_snapshot_delegate_impl.h | 54 + .../distributeddb/storage/include/ikvdb.h | 61 + .../storage/include/ikvdb_connection.h | 130 + .../storage/include/ikvdb_factory.h | 64 + .../storage/include/ikvdb_result_set.h | 52 + .../storage/include/ikvdb_snapshot.h | 35 + .../storage/include/ikvdb_sync_interface.h | 64 + .../storage/include/kvdb_commit_notify_data.h | 57 + .../storage/include/kvdb_conflict_entry.h | 36 + .../storage/include/kvdb_manager.h | 136 + .../storage/include/kvdb_pragma.h | 68 + .../storage/include/kvdb_properties.h | 107 + .../storage/include/multi_ver_def.h | 80 + .../include/multi_ver_kvdb_sync_interface.h | 73 + .../storage/include/multi_ver_vacuum.h | 147 + .../include/multi_ver_vacuum_executor.h | 82 + .../include/single_ver_kvdb_sync_interface.h | 68 + .../storage/include/storage_engine_manager.h | 84 + .../storage/src/default_factory.cpp | 96 + .../storage/src/default_factory.h | 53 + .../storage/src/generic_kvdb.cpp | 396 +++ .../distributeddb/storage/src/generic_kvdb.h | 192 ++ .../storage/src/generic_kvdb_connection.cpp | 325 +++ .../storage/src/generic_kvdb_connection.h | 112 + .../src/generic_single_ver_kv_entry.cpp | 327 +++ .../storage/src/generic_single_ver_kv_entry.h | 85 + .../distributeddb/storage/src/ikvdb_commit.h | 43 + .../storage/src/ikvdb_commit_storage.h | 65 + .../storage/src/ikvdb_factory.cpp | 35 + .../storage/src/ikvdb_raw_cursor.h | 47 + .../kvdb_commit_notify_filterable_data.cpp | 103 + .../src/kvdb_commit_notify_filterable_data.h | 68 + .../storage/src/kvdb_manager.cpp | 789 ++++++ .../storage/src/kvdb_observer_handle.cpp | 45 + .../storage/src/kvdb_observer_handle.h | 38 + .../storage/src/kvdb_properties.cpp | 144 + .../distributeddb/storage/src/kvdb_utils.cpp | 86 + .../distributeddb/storage/src/kvdb_utils.h | 38 + .../storage/src/kvdb_windowed_result_set.cpp | 58 + .../storage/src/kvdb_windowed_result_set.h | 54 + .../distributeddb/storage/src/local_kvdb.h | 31 + .../multiver/generic_multi_ver_kv_entry.cpp | 156 ++ .../src/multiver/generic_multi_ver_kv_entry.h | 67 + .../multiver/ikvdb_multi_ver_data_storage.h | 68 + .../multiver/ikvdb_multi_ver_transaction.h | 59 + .../storage/src/multiver/multi_ver_commit.cpp | 124 + .../storage/src/multiver/multi_ver_commit.h | 71 + .../storage/src/multiver/multi_ver_kv_entry.h | 40 + .../src/multiver/multi_ver_kvdata_storage.cpp | 559 ++++ .../src/multiver/multi_ver_kvdata_storage.h | 94 + .../src/multiver/multi_ver_natural_store.cpp | 1182 ++++++++ .../src/multiver/multi_ver_natural_store.h | 203 ++ ...i_ver_natural_store_commit_notify_data.cpp | 110 + ...lti_ver_natural_store_commit_notify_data.h | 59 + ...multi_ver_natural_store_commit_storage.cpp | 914 ++++++ .../multi_ver_natural_store_commit_storage.h | 127 + .../multi_ver_natural_store_connection.cpp | 526 ++++ .../multi_ver_natural_store_connection.h | 126 + .../multi_ver_natural_store_snapshot.cpp | 66 + .../multi_ver_natural_store_snapshot.h | 45 + .../multi_ver_natural_store_transfer_data.cpp | 53 + .../multi_ver_natural_store_transfer_data.h | 40 + .../src/multiver/multi_ver_storage_engine.cpp | 66 + .../src/multiver/multi_ver_storage_engine.h | 50 + .../multiver/multi_ver_storage_executor.cpp | 1500 ++++++++++ .../src/multiver/multi_ver_storage_executor.h | 189 ++ .../storage/src/multiver/multi_ver_vacuum.cpp | 674 +++++ .../multi_ver_vacuum_executor_impl.cpp | 328 +++ .../multiver/multi_ver_vacuum_executor_impl.h | 74 + .../src/multiver/multi_ver_value_object.cpp | 148 + .../src/multiver/multi_ver_value_object.h | 66 + .../storage/src/operation/database_oper.cpp | 582 ++++ .../storage/src/operation/database_oper.h | 106 + .../src/operation/local_database_oper.cpp | 214 ++ .../src/operation/local_database_oper.h | 57 + .../src/operation/multi_ver_database_oper.cpp | 290 ++ .../src/operation/multi_ver_database_oper.h | 64 + .../operation/single_ver_database_oper.cpp | 559 ++++ .../src/operation/single_ver_database_oper.h | 79 + .../storage/src/package_file.cpp | 571 ++++ .../distributeddb/storage/src/package_file.h | 38 + .../storage/src/result_entries_window.cpp | 186 ++ .../storage/src/result_entries_window.h | 50 + .../storage/src/single_ver_kv_entry.h | 39 + ...e_ver_natural_store_commit_notify_data.cpp | 280 ++ ...gle_ver_natural_store_commit_notify_data.h | 114 + .../storage/src/sqlite/query_object.cpp | 736 +++++ .../storage/src/sqlite/query_object.h | 78 + .../storage/src/sqlite/sqlite_import.h | 25 + .../storage/src/sqlite/sqlite_local_kvdb.cpp | 402 +++ .../storage/src/sqlite/sqlite_local_kvdb.h | 93 + .../sqlite/sqlite_local_kvdb_connection.cpp | 498 ++++ .../src/sqlite/sqlite_local_kvdb_connection.h | 111 + .../src/sqlite/sqlite_local_kvdb_snapshot.cpp | 54 + .../src/sqlite/sqlite_local_kvdb_snapshot.h | 49 + .../sqlite/sqlite_local_storage_engine.cpp | 30 + .../src/sqlite/sqlite_local_storage_engine.h | 36 + .../sqlite/sqlite_local_storage_executor.cpp | 252 ++ .../sqlite/sqlite_local_storage_executor.h | 81 + .../sqlite/sqlite_multi_ver_data_storage.cpp | 396 +++ .../sqlite/sqlite_multi_ver_data_storage.h | 89 + .../sqlite/sqlite_multi_ver_transaction.cpp | 1524 ++++++++++ .../src/sqlite/sqlite_multi_ver_transaction.h | 213 ++ .../sqlite_single_ver_database_upgrader.cpp | 350 +++ .../sqlite_single_ver_database_upgrader.h | 56 + .../sqlite_single_ver_forward_cursor.cpp | 145 + .../sqlite/sqlite_single_ver_forward_cursor.h | 63 + .../sqlite_single_ver_natural_store.cpp | 1811 ++++++++++++ .../sqlite/sqlite_single_ver_natural_store.h | 273 ++ ...te_single_ver_natural_store_connection.cpp | 1693 +++++++++++ ...lite_single_ver_natural_store_connection.h | 221 ++ .../sqlite/sqlite_single_ver_result_set.cpp | 306 ++ .../src/sqlite/sqlite_single_ver_result_set.h | 104 + ...te_single_ver_schema_database_upgrader.cpp | 237 ++ ...lite_single_ver_schema_database_upgrader.h | 40 + .../sqlite_single_ver_storage_engine.cpp | 1069 +++++++ .../sqlite/sqlite_single_ver_storage_engine.h | 119 + .../sqlite_single_ver_storage_executor.cpp | 1906 +++++++++++++ .../sqlite_single_ver_storage_executor.h | 355 +++ ...lite_single_ver_storage_executor_cache.cpp | 874 ++++++ .../sqlite_single_ver_storage_executor_sql.h | 220 ++ .../src/sqlite/sqlite_storage_engine.cpp | 200 ++ .../src/sqlite/sqlite_storage_engine.h | 74 + .../src/sqlite/sqlite_storage_executor.cpp | 56 + .../src/sqlite/sqlite_storage_executor.h | 41 + .../storage/src/sqlite/sqlite_utils.cpp | 1402 ++++++++++ .../storage/src/sqlite/sqlite_utils.h | 163 ++ .../storage/src/storage_engine.cpp | 426 +++ .../storage/src/storage_engine.h | 127 + .../storage/src/storage_engine_manager.cpp | 312 +++ .../storage/src/storage_executor.cpp | 51 + .../storage/src/storage_executor.h | 55 + .../storage/src/sync_able_kvdb.cpp | 235 ++ .../storage/src/sync_able_kvdb.h | 104 + .../storage/src/sync_able_kvdb_connection.cpp | 292 ++ .../storage/src/sync_able_kvdb_connection.h | 70 + .../storage/src/upgrader/database_upgrader.h | 26 + .../upgrader/single_ver_database_upgrader.cpp | 74 + .../upgrader/single_ver_database_upgrader.h | 40 + .../single_ver_schema_database_upgrader.cpp | 103 + .../single_ver_schema_database_upgrader.h | 46 + .../syncer/include/commit_history_sync.h | 146 + .../syncer/include/device_manager.h | 71 + .../syncer/include/isync_engine.h | 63 + .../syncer/include/isync_state_machine.h | 60 + .../syncer/include/isync_target.h | 57 + .../syncer/include/isync_task_context.h | 157 ++ .../distributeddb/syncer/include/isyncer.h | 85 + .../distributeddb/syncer/include/meta_data.h | 109 + .../syncer/include/multi_ver_data_sync.h | 135 + .../syncer/include/sync_operation.h | 155 ++ .../distributeddb/syncer/include/sync_types.h | 34 + .../syncer/include/syncer_factory.h | 33 + .../syncer/include/syncer_proxy.h | 91 + .../syncer/include/time_helper.h | 69 + .../syncer/include/value_slice_sync.h | 126 + .../distributeddb/syncer/src/ability_sync.cpp | 931 +++++++ .../distributeddb/syncer/src/ability_sync.h | 185 ++ .../syncer/src/commit_history_sync.cpp | 714 +++++ .../syncer/src/device_manager.cpp | 157 ++ .../syncer/src/generic_syncer.cpp | 581 ++++ .../distributeddb/syncer/src/generic_syncer.h | 154 + .../distributeddb/syncer/src/meta_data.cpp | 312 +++ .../syncer/src/multi_ver_data_sync.cpp | 690 +++++ .../syncer/src/multi_ver_sync_engine.cpp | 28 + .../syncer/src/multi_ver_sync_engine.h | 41 + .../src/multi_ver_sync_state_machine.cpp | 587 ++++ .../syncer/src/multi_ver_sync_state_machine.h | 138 + .../syncer/src/multi_ver_sync_target.h | 26 + .../src/multi_ver_sync_task_context.cpp | 234 ++ .../syncer/src/multi_ver_sync_task_context.h | 106 + .../syncer/src/multi_ver_syncer.cpp | 123 + .../syncer/src/multi_ver_syncer.h | 60 + .../syncer/src/single_ver_data_sync.cpp | 1865 +++++++++++++ .../syncer/src/single_ver_data_sync.h | 307 ++ ...ngle_ver_data_sync_with_sliding_window.cpp | 66 + ...single_ver_data_sync_with_sliding_window.h | 45 + .../syncer/src/single_ver_sync_engine.cpp | 46 + .../syncer/src/single_ver_sync_engine.h | 43 + .../src/single_ver_sync_state_machine.cpp | 1140 ++++++++ .../src/single_ver_sync_state_machine.h | 220 ++ .../syncer/src/single_ver_sync_target.cpp | 53 + .../syncer/src/single_ver_sync_target.h | 44 + .../src/single_ver_sync_task_context.cpp | 352 +++ .../syncer/src/single_ver_sync_task_context.h | 159 ++ .../syncer/src/single_ver_syncer.cpp | 159 ++ .../syncer/src/single_ver_syncer.h | 51 + .../syncer/src/sliding_window_receiver.cpp | 245 ++ .../syncer/src/sliding_window_receiver.h | 67 + .../syncer/src/sliding_window_sender.cpp | 286 ++ .../syncer/src/sliding_window_sender.h | 75 + .../distributeddb/syncer/src/sync_engine.cpp | 644 +++++ .../distributeddb/syncer/src/sync_engine.h | 150 + .../syncer/src/sync_operation.cpp | 191 ++ .../syncer/src/sync_state_machine.cpp | 354 +++ .../syncer/src/sync_state_machine.h | 143 + .../distributeddb/syncer/src/sync_target.cpp | 82 + .../distributeddb/syncer/src/sync_target.h | 58 + .../syncer/src/sync_task_context.cpp | 592 ++++ .../syncer/src/sync_task_context.h | 243 ++ .../syncer/src/syncer_factory.cpp | 37 + .../distributeddb/syncer/src/syncer_proxy.cpp | 162 ++ .../distributeddb/syncer/src/time_helper.cpp | 114 + .../distributeddb/syncer/src/time_sync.cpp | 553 ++++ .../libs/distributeddb/syncer/src/time_sync.h | 132 + .../syncer/src/value_slice_sync.cpp | 629 +++++ .../libs/distributeddb/test/BUILD.gn | 666 +++++ .../common/distributeddb_auto_launch_test.cpp | 986 +++++++ .../distributeddb_data_generate_unit_test.cpp | 93 + .../distributeddb_data_generate_unit_test.h | 111 + .../distributeddb_json_precheck_unit_test.cpp | 195 ++ .../distributeddb_notification_chain_test.cpp | 183 ++ .../common/distributeddb_parcel_unit_test.cpp | 679 +++++ .../distributeddb_schema_object_test.cpp | 1082 ++++++++ .../common/distributeddb_schema_unit_test.cpp | 513 ++++ .../common/distributeddb_tools_unit_test.cpp | 761 +++++ .../common/distributeddb_tools_unit_test.h | 241 ++ .../common/common/evloop_timer_unit_test.cpp | 416 +++ .../common/process_communicator_test_stub.h | 93 + .../common/communicator/adapter_stub.cpp | 363 +++ .../common/communicator/adapter_stub.h | 127 + .../distributeddb_communicator_common.cpp | 344 +++ .../distributeddb_communicator_common.h | 96 + .../distributeddb_communicator_deep_test.cpp | 557 ++++ ...buteddb_communicator_send_receive_test.cpp | 675 +++++ .../distributeddb_communicator_test.cpp | 761 +++++ ...tributeddb_interfaces_auto_launch_test.cpp | 725 +++++ ..._interfaces_data_operation_syncdb_test.cpp | 1381 +++++++++ ...buteddb_interfaces_data_operation_test.cpp | 1788 ++++++++++++ ...teddb_interfaces_database_corrupt_test.cpp | 354 +++ ...distributeddb_interfaces_database_test.cpp | 1351 +++++++++ ...eddb_interfaces_device_identifier_test.cpp | 313 +++ ...teddb_interfaces_encrypt_database_test.cpp | 496 ++++ ...teddb_interfaces_encrypt_delegate_test.cpp | 1112 ++++++++ ...eddb_interfaces_import_and_export_test.cpp | 1132 ++++++++ ...stributeddb_interfaces_index_unit_test.cpp | 851 ++++++ ...nterfaces_nb_delegate_local_batch_test.cpp | 1137 ++++++++ ...interfaces_nb_delegate_schema_put_test.cpp | 359 +++ ...tributeddb_interfaces_nb_delegate_test.cpp | 1945 +++++++++++++ ...stributeddb_interfaces_nb_publish_test.cpp | 792 ++++++ ...buteddb_interfaces_nb_transaction_test.cpp | 1104 ++++++++ ...ributeddb_interfaces_nb_unpublish_test.cpp | 480 ++++ .../distributeddb_interfaces_query_test.cpp | 226 ++ ...uteddb_interfaces_register_syncdb_test.cpp | 1872 +++++++++++++ ...teddb_interfaces_resultset_performance.cpp | 174 ++ ...nterfaces_schema_database_upgrade_test.cpp | 380 +++ ...erfaces_single_version_result_set_test.cpp | 534 ++++ ...teddb_interfaces_space_management_test.cpp | 524 ++++ ...terfaces_transaction_optimization_test.cpp | 811 ++++++ ...ddb_interfaces_transaction_syncdb_test.cpp | 642 +++++ ...tributeddb_interfaces_transaction_test.cpp | 674 +++++ ...uteddb_interfaces_transaction_testcase.cpp | 720 +++++ ...ibuteddb_interfaces_transaction_testcase.h | 85 + .../process_system_api_adapter_impl.cpp | 134 + .../process_system_api_adapter_impl.h | 48 + ...t_process_system_api_adapter_impl_test.cpp | 222 ++ .../distributeddb_file_package_test.cpp | 343 +++ .../distributeddb_multi_ver_vacuum_test.cpp | 751 +++++ .../distributeddb_sqlite_register_test.cpp | 207 ++ ...tributeddb_storage_commit_storage_test.cpp | 839 ++++++ ...tributeddb_storage_data_operation_test.cpp | 642 +++++ .../distributeddb_storage_encrypt_test.cpp | 1373 +++++++++ ...tributeddb_storage_index_optimize_test.cpp | 363 +++ ..._memory_single_ver_naturall_store_test.cpp | 1039 +++++++ ...buteddb_storage_register_conflict_test.cpp | 835 ++++++ ...buteddb_storage_register_observer_test.cpp | 827 ++++++ ...db_storage_resultset_and_json_optimize.cpp | 313 +++ ...rage_single_ver_natural_store_testcase.cpp | 1847 ++++++++++++ ...torage_single_ver_natural_store_testcase.h | 155 ++ ...uteddb_storage_single_ver_upgrade_test.cpp | 589 ++++ ...e_sqlite_single_ver_natural_store_test.cpp | 1055 +++++++ ...ibuteddb_storage_transaction_data_test.cpp | 1592 +++++++++++ ...uteddb_storage_transaction_record_test.cpp | 1100 ++++++++ .../multi_ver_vacuum_executor_stub.cpp | 127 + .../storage/multi_ver_vacuum_executor_stub.h | 60 + .../distributeddb_ability_sync_test.cpp | 543 ++++ .../distributeddb_anti_dos_sync_test.cpp | 311 +++ .../distributeddb_multi_ver_p2p_sync_test.cpp | 1652 +++++++++++ ...buteddb_single_ver_p2p_sync_check_test.cpp | 485 ++++ ...distributeddb_single_ver_p2p_sync_test.cpp | 2034 ++++++++++++++ ...stributeddb_syncer_device_manager_test.cpp | 240 ++ .../syncer/distributeddb_time_sync_test.cpp | 480 ++++ .../virtual_multi_ver_sync_db_interface.cpp | 228 ++ .../virtual_multi_ver_sync_db_interface.h | 100 + .../virtual_single_ver_sync_db_Interface.cpp | 315 +++ .../virtual_single_ver_sync_db_Interface.h | 119 + .../syncer/virtual_time_sync_communicator.cpp | 134 + .../syncer/virtual_time_sync_communicator.h | 77 + .../common/syncer/vitural_communicator.cpp | 154 + .../common/syncer/vitural_communicator.h | 97 + .../vitural_communicator_aggregator.cpp | 224 ++ .../syncer/vitural_communicator_aggregator.h | 88 + .../unittest/common/syncer/vitural_device.cpp | 264 ++ .../unittest/common/syncer/vitural_device.h | 64 + .../sa_profile/1301.xml | 26 + services/distributeddataservice/test/BUILD.gn | 516 ++++ .../include/distributed_test_sysinfo.h | 104 + .../include/distributed_test_tools.h | 576 ++++ .../include/distributeddb_data_generator.h | 548 ++++ .../include/distributeddb_log_print.h | 32 + .../include/distributeddb_nb_test_tools.h | 325 +++ .../include/distributeddb_schema_test_tools.h | 185 ++ .../src/distributed_test_sysinfo.cpp | 245 ++ .../src/distributed_test_tools.cpp | 2056 ++++++++++++++ .../src/distributeddb_data_generator.cpp | 599 ++++ .../src/distributeddb_nb_test_tools.cpp | 556 ++++ .../src/distributeddb_schema_test_tools.cpp | 121 + .../distributed_crud_transaction_tools.h | 65 + .../distributeddb_nb_cursor_testcase.h | 55 + .../include/process_communicator_test_stub.h | 97 + .../distributed_crud_transaction_tools.cpp | 275 ++ .../src/distributeddb_kv_backup_test.cpp | 1226 ++++++++ .../src/distributeddb_kv_batch_crud_test.cpp | 1084 ++++++++ ...distributeddb_kv_concurrency_crud_test.cpp | 320 +++ .../src/distributeddb_kv_create_test.cpp | 1977 +++++++++++++ .../src/distributeddb_kv_crud_test.cpp | 721 +++++ .../distributeddb_kv_observer_snap_test.cpp | 1581 +++++++++++ .../src/distributeddb_kv_observer_test.cpp | 1684 +++++++++++ .../src/distributeddb_kv_realdel_test.cpp | 373 +++ ...distributeddb_kv_transaction_perf_test.cpp | 340 +++ .../src/distributeddb_kv_transaction_test.cpp | 1998 +++++++++++++ .../src/distributeddb_nb_backup_test.cpp | 1457 ++++++++++ .../src/distributeddb_nb_batch_crud_test.cpp | 2472 +++++++++++++++++ .../src/distributeddb_nb_create_test.cpp | 1913 +++++++++++++ .../src/distributeddb_nb_crud_test.cpp | 1961 +++++++++++++ .../src/distributeddb_nb_cursor_test.cpp | 790 ++++++ .../src/distributeddb_nb_cursor_testcase.cpp | 2029 ++++++++++++++ ...teddb_nb_enable_sync_by_closed_db_test.cpp | 763 +++++ ...distributeddb_nb_local_batch_crud_test.cpp | 1035 +++++++ .../src/distributeddb_nb_observer_test.cpp | 2223 +++++++++++++++ ...buteddb_nb_predicate_query_expand_test.cpp | 1188 ++++++++ .../distributeddb_nb_predicate_query_test.cpp | 2083 ++++++++++++++ .../src/distributeddb_nb_schema_test.cpp | 2417 ++++++++++++++++ .../config/trans_permission.json | 130 + .../distributeddataservice/ohos_test.xml | 22 + test/resource/distributeddb/baseline.xml | 32 + test/resource/distributeddb/ohos_test.xml | 22 + 766 files changed, 213317 insertions(+), 65 deletions(-) create mode 100644 .gitattributes create mode 100755 BUILD.gn create mode 100644 LICENSE delete mode 100644 README.en.md create mode 100644 README_zh.md create mode 100644 figures/en-us_image_0000001162536643.png create mode 100644 figures/zh-cn_image_0000001162536643.png create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/idevice_status_change_listener.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_client_death_observer.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_data_service.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_observer.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_resultset.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_single.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_snapshot.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_sync_callback.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/include/inner_types.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_blob.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_change_notification.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/blob.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/change_notification.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/data_query.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/delegate_mgr_callback.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/distributed_kv_data_manager.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/idevice_status_change_listener_impl.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_client_death_observer.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_data_service.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_observer.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_resultset.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_single.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_snapshot.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_sync_callback.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_nb_impl.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.h create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.h create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/BUILD.gn create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_blob_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_conflict_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_data_manager_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_store_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/blob_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_encrypt_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_test.cpp create mode 100644 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_client_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_snapshot_client_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/local_subscribe_store_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_query_test.cpp create mode 100755 frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_test.cpp create mode 100755 interfaces/innerkits/app_distributeddata/BUILD.gn create mode 100755 interfaces/innerkits/app_distributeddata/include/app_blob.h create mode 100644 interfaces/innerkits/app_distributeddata/include/app_change_notification.h create mode 100755 interfaces/innerkits/app_distributeddata/include/app_device_status_change_listener.h create mode 100755 interfaces/innerkits/app_distributeddata/include/app_distributed_kv_data_manager.h create mode 100755 interfaces/innerkits/app_distributeddata/include/app_kvstore.h create mode 100644 interfaces/innerkits/app_distributeddata/include/app_kvstore_conflict_data.h create mode 100644 interfaces/innerkits/app_distributeddata/include/app_kvstore_corruption_observer.h create mode 100644 interfaces/innerkits/app_distributeddata/include/app_kvstore_observer.h create mode 100755 interfaces/innerkits/app_distributeddata/include/app_kvstore_result_set.h create mode 100755 interfaces/innerkits/app_distributeddata/include/app_types.h create mode 100644 interfaces/innerkits/app_distributeddata/include/visibility.h create mode 100755 interfaces/innerkits/distributeddata/BUILD.gn create mode 100644 interfaces/innerkits/distributeddata/include/blob.h create mode 100644 interfaces/innerkits/distributeddata/include/change_notification.h create mode 100755 interfaces/innerkits/distributeddata/include/data_query.h create mode 100644 interfaces/innerkits/distributeddata/include/device_status_change_listener.h create mode 100644 interfaces/innerkits/distributeddata/include/distributed_kv_data_manager.h create mode 100644 interfaces/innerkits/distributeddata/include/kvstore.h create mode 100644 interfaces/innerkits/distributeddata/include/kvstore_death_recipient.h create mode 100644 interfaces/innerkits/distributeddata/include/kvstore_observer.h create mode 100644 interfaces/innerkits/distributeddata/include/kvstore_result_set.h create mode 100755 interfaces/innerkits/distributeddata/include/kvstore_snapshot.h create mode 100644 interfaces/innerkits/distributeddata/include/kvstore_sync_callback.h create mode 100755 interfaces/innerkits/distributeddata/include/single_kvstore.h create mode 100755 interfaces/innerkits/distributeddata/include/types.h create mode 100644 interfaces/innerkits/distributeddata/include/visibility.h create mode 100755 ohos.build create mode 100755 services/distributeddataservice/adapter/BUILD.gn create mode 100644 services/distributeddataservice/adapter/LICENSE create mode 100755 services/distributeddataservice/adapter/account/BUILD.gn create mode 100755 services/distributeddataservice/adapter/account/src/account_delegate.cpp create mode 100755 services/distributeddataservice/adapter/account/src/account_delegate_impl.cpp create mode 100755 services/distributeddataservice/adapter/account/src/account_delegate_impl.h create mode 100755 services/distributeddataservice/adapter/account/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/account/test/account_delegate_test.cpp create mode 100755 services/distributeddataservice/adapter/autils/BUILD.gn create mode 100755 services/distributeddataservice/adapter/autils/src/constant.cpp create mode 100755 services/distributeddataservice/adapter/autils/src/directory_utils.cpp create mode 100755 services/distributeddataservice/adapter/autils/src/kv_scheduler.cpp create mode 100755 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_task.cpp create mode 100755 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.cpp create mode 100644 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.h create mode 100755 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool.cpp create mode 100755 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.cpp create mode 100644 services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.h create mode 100755 services/distributeddataservice/adapter/autils/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/autils/test/unittest/concurrent_map_test.cpp create mode 100755 services/distributeddataservice/adapter/autils/test/unittest/kv_scheduler_test.cpp create mode 100644 services/distributeddataservice/adapter/autils/test/unittest/kv_store_thread_pool_test.cpp create mode 100755 services/distributeddataservice/adapter/broadcaster/BUILD.gn create mode 100755 services/distributeddataservice/adapter/broadcaster/src/broadcast_sender.cpp create mode 100755 services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.cpp create mode 100644 services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.h create mode 100755 services/distributeddataservice/adapter/communicator/BUILD.gn create mode 100755 services/distributeddataservice/adapter/communicator/src/app_device_handler.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/app_device_handler.h create mode 100755 services/distributeddataservice/adapter/communicator/src/app_pipe_handler.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/app_pipe_handler.h create mode 100755 services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.h create mode 100755 services/distributeddataservice/adapter/communicator/src/ark_communication_provider.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/ark_communication_provider.h create mode 100644 services/distributeddataservice/adapter/communicator/src/communication_provider.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/communication_provider_impl.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/communication_provider_impl.h create mode 100755 services/distributeddataservice/adapter/communicator/src/data_buffer.cpp create mode 100644 services/distributeddataservice/adapter/communicator/src/data_buffer.h create mode 100755 services/distributeddataservice/adapter/communicator/src/process_communicator_impl.cpp create mode 100755 services/distributeddataservice/adapter/communicator/src/softbus_adapter.h create mode 100755 services/distributeddataservice/adapter/communicator/src/softbus_adapter_standard.cpp create mode 100755 services/distributeddataservice/adapter/communicator/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/communicator/test/unittest/communication_provider_impl_test.cpp create mode 100755 services/distributeddataservice/adapter/dfx/BUILD.gn create mode 100755 services/distributeddataservice/adapter/dfx/src/dds_trace.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/dfx_code_constant.h create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/fault_reporter.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/hiview_adapter.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/hiview_adapter.h create mode 100755 services/distributeddataservice/adapter/dfx/src/reporter.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/statistic_reporter.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.h create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.cpp create mode 100755 services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.h create mode 100644 services/distributeddataservice/adapter/dfx/src/value_hash.h create mode 100755 services/distributeddataservice/adapter/dfx/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_mst_test.cpp create mode 100755 services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_ut_test.cpp create mode 100755 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/fake_hiview.cpp create mode 100755 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hievent.cpp create mode 100755 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hiview.cpp create mode 100644 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/fake_hiview.h create mode 100644 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hievent.h create mode 100644 services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hiview.h create mode 100755 services/distributeddataservice/adapter/include/account/account_delegate.h create mode 100644 services/distributeddataservice/adapter/include/account/visibility.h create mode 100644 services/distributeddataservice/adapter/include/autils/concurrent_map.h create mode 100644 services/distributeddataservice/adapter/include/autils/constant.h create mode 100644 services/distributeddataservice/adapter/include/autils/directory_utils.h create mode 100644 services/distributeddataservice/adapter/include/autils/kv_scheduler.h create mode 100644 services/distributeddataservice/adapter/include/autils/kv_store_task.h create mode 100755 services/distributeddataservice/adapter/include/autils/kv_store_thread_pool.h create mode 100644 services/distributeddataservice/adapter/include/autils/platform_specific.h create mode 100755 services/distributeddataservice/adapter/include/autils/serializable.h create mode 100755 services/distributeddataservice/adapter/include/autils/time_utils.h create mode 100644 services/distributeddataservice/adapter/include/autils/visibility.h create mode 100644 services/distributeddataservice/adapter/include/broadcaster/broadcast_sender.h create mode 100644 services/distributeddataservice/adapter/include/broadcaster/visibility.h create mode 100644 services/distributeddataservice/adapter/include/communicator/app_data_change_listener.h create mode 100644 services/distributeddataservice/adapter/include/communicator/communication_provider.h create mode 100644 services/distributeddataservice/adapter/include/communicator/idevice_query.h create mode 100755 services/distributeddataservice/adapter/include/communicator/process_communicator_impl.h create mode 100755 services/distributeddataservice/adapter/include/dfx/db_meta_callback_delegate.h create mode 100755 services/distributeddataservice/adapter/include/dfx/dds_trace.h create mode 100755 services/distributeddataservice/adapter/include/dfx/dfx_types.h create mode 100644 services/distributeddataservice/adapter/include/dfx/fault_reporter.h create mode 100755 services/distributeddataservice/adapter/include/dfx/reporter.h create mode 100644 services/distributeddataservice/adapter/include/dfx/statistic_reporter.h create mode 100644 services/distributeddataservice/adapter/include/dfx/visibility.h create mode 100755 services/distributeddataservice/adapter/include/log/log_print.h create mode 100644 services/distributeddataservice/adapter/include/log/visibility.h create mode 100755 services/distributeddataservice/adapter/include/permission/permission_validator.h create mode 100644 services/distributeddataservice/adapter/include/permission/visibility.h create mode 100755 services/distributeddataservice/adapter/include/security/security_adapter.h create mode 100644 services/distributeddataservice/adapter/include/utils/crypto_utils.h create mode 100755 services/distributeddataservice/adapter/include/utils/json_utils.h create mode 100755 services/distributeddataservice/adapter/include/utils/kvstore_utils.h create mode 100755 services/distributeddataservice/adapter/permission/BUILD.gn create mode 100755 services/distributeddataservice/adapter/permission/src/client_permission_validator.cpp create mode 100755 services/distributeddataservice/adapter/permission/src/client_permission_validator.h create mode 100755 services/distributeddataservice/adapter/permission/src/permission_validator.cpp create mode 100755 services/distributeddataservice/adapter/permission/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/permission/test/unittest/permission_validator_test.cpp create mode 100755 services/distributeddataservice/adapter/security/BUILD.gn create mode 100755 services/distributeddataservice/adapter/security/src/block_integer.cpp create mode 100755 services/distributeddataservice/adapter/security/src/block_integer.h create mode 100755 services/distributeddataservice/adapter/security/src/security.cpp create mode 100644 services/distributeddataservice/adapter/security/src/security.h create mode 100755 services/distributeddataservice/adapter/security/src/security_adapter.cpp create mode 100755 services/distributeddataservice/adapter/security/src/sensitive.cpp create mode 100755 services/distributeddataservice/adapter/security/src/sensitive.h create mode 100755 services/distributeddataservice/adapter/test/BUILD.gn create mode 100755 services/distributeddataservice/adapter/utils/BUILD.gn create mode 100755 services/distributeddataservice/adapter/utils/src/crypto_utils.cpp create mode 100755 services/distributeddataservice/adapter/utils/src/kvstore_utils.cpp create mode 100755 services/distributeddataservice/app/BUILD.gn create mode 100644 services/distributeddataservice/app/distributed_data.rc create mode 100755 services/distributeddataservice/app/src/backup_handler.cpp create mode 100755 services/distributeddataservice/app/src/backup_handler.h create mode 100644 services/distributeddataservice/app/src/device_change_listener_impl.cpp create mode 100755 services/distributeddataservice/app/src/device_change_listener_impl.h create mode 100644 services/distributeddataservice/app/src/device_kvstore_impl.cpp create mode 100755 services/distributeddataservice/app/src/device_kvstore_impl.h create mode 100644 services/distributeddataservice/app/src/device_kvstore_observer_impl.cpp create mode 100644 services/distributeddataservice/app/src/device_kvstore_observer_impl.h create mode 100644 services/distributeddataservice/app/src/device_kvstore_resultset_impl.cpp create mode 100755 services/distributeddataservice/app/src/device_kvstore_resultset_impl.h create mode 100755 services/distributeddataservice/app/src/flowctrl_manager/BUILD.gn create mode 100755 services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.cpp create mode 100755 services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.h create mode 100755 services/distributeddataservice/app/src/kvstore_account_observer.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_account_observer.h create mode 100755 services/distributeddataservice/app/src/kvstore_app_accessor.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_app_accessor.h create mode 100755 services/distributeddataservice/app/src/kvstore_app_manager.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_app_manager.h create mode 100755 services/distributeddataservice/app/src/kvstore_common.h create mode 100644 services/distributeddataservice/app/src/kvstore_data_service.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_data_service.h create mode 100755 services/distributeddataservice/app/src/kvstore_impl.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_impl.h create mode 100755 services/distributeddataservice/app/src/kvstore_meta_manager.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_meta_manager.h create mode 100644 services/distributeddataservice/app/src/kvstore_observer_impl.cpp create mode 100644 services/distributeddataservice/app/src/kvstore_observer_impl.h create mode 100755 services/distributeddataservice/app/src/kvstore_resultset_impl.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_resultset_impl.h create mode 100755 services/distributeddataservice/app/src/kvstore_snapshot_impl.cpp create mode 100644 services/distributeddataservice/app/src/kvstore_snapshot_impl.h create mode 100755 services/distributeddataservice/app/src/kvstore_sync_manager.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_sync_manager.h create mode 100755 services/distributeddataservice/app/src/kvstore_user_manager.cpp create mode 100755 services/distributeddataservice/app/src/kvstore_user_manager.h create mode 100644 services/distributeddataservice/app/src/query_helper.cpp create mode 100755 services/distributeddataservice/app/src/query_helper.h create mode 100755 services/distributeddataservice/app/src/single_kvstore_impl.cpp create mode 100755 services/distributeddataservice/app/src/single_kvstore_impl.h create mode 100755 services/distributeddataservice/app/src/uninstaller/BUILD.gn create mode 100755 services/distributeddataservice/app/src/uninstaller/uninstaller.cpp create mode 100755 services/distributeddataservice/app/src/uninstaller/uninstaller.h create mode 100755 services/distributeddataservice/app/src/uninstaller/uninstaller_impl.cpp create mode 100755 services/distributeddataservice/app/src/uninstaller/uninstaller_impl.h create mode 100755 services/distributeddataservice/app/test/BUILD.gn create mode 100644 services/distributeddataservice/app/test/moduletest/distributeddata_account_event_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_app_manager.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_backup_test.cpp create mode 100644 services/distributeddataservice/app/test/unittest/kvstore_data_service.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_data_service_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_flowctrl_manager_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_impl_logical_isolation_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/kvstore_impl_physical_isolation_test.cpp create mode 100644 services/distributeddataservice/app/test/unittest/kvstore_sync_manager_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/uninstaller_test.cpp create mode 100755 services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.cpp create mode 100755 services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/BUILD.gn create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/auto_launch.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/db_common.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/db_constant.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/db_errno.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/db_types.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/endian_convert.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/hash.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/json_object.h create mode 100644 services/distributeddataservice/libs/distributeddb/common/include/log_print.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/macro_utils.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/notification_chain.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/param_check_utils.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/parcel.h create mode 100644 services/distributeddataservice/libs/distributeddb/common/include/performance_analysis.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/platform_specific.h create mode 100644 services/distributeddataservice/libs/distributeddb/common/include/ref_object.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/res_finalizer.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/runtime_context.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/schema_object.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/schema_utils.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/semaphore.h create mode 100644 services/distributeddataservice/libs/distributeddb/common/include/task_pool.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/value_hash_calc.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/value_object.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/include/version.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/auto_launch.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/db_constant.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/include/event_fd.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent_loop.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent_loop.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/flatbuffer_schema.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/hash.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/notification_chain.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/param_check_utils.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/parcel.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/performance_analysis.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/query.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/query_expression.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/ref_object.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/runtime_context.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/semaphore.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/task_pool.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/task_queue.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/task_queue.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.h create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/types_export.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/common/src/value_object.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/combine_status.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/communicator_aggregator.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/communicator_type_define.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/frame_combiner.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/frame_retainer.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/iadapter.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator_aggregator.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/message.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/message_transform.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/network_adapter.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/object_holder.h create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/include/object_holder_typed.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/parse_result.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/include/send_task_scheduler.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/combine_status.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/communicator.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/src/communicator.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/communicator_aggregator.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/frame_combiner.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/src/frame_header.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/frame_retainer.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/message_transform.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/network_adapter.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.h create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/send_task_scheduler.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.h create mode 100755 services/distributeddataservice/libs/distributeddb/include/auto_launch_export.h create mode 100755 services/distributeddataservice/libs/distributeddb/include/query.h create mode 100755 services/distributeddataservice/libs/distributeddb/include/query_expression.h create mode 100755 services/distributeddataservice/libs/distributeddb/include/types_export.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/get_query_info.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_communicator.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_system_api_adapter.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_changed_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate_manager.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_errno.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_conflict_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_delegate.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_observer.h create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_result_set.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_snapshot_delegate.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/include/types.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_manager.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_errno.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_connection.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_factory.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_result_set.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_snapshot.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_sync_interface.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/kvdb_commit_notify_data.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/kvdb_conflict_entry.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/kvdb_manager.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/kvdb_pragma.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/kvdb_properties.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_def.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/single_ver_kvdb_sync_interface.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/include/storage_engine_manager.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/default_factory.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/default_factory.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit_storage.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_factory.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_raw_cursor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_manager.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_properties.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/local_kvdb.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kv_entry.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/package_file.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/package_file.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/single_ver_kv_entry.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_import.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/storage_engine_manager.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.h create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/upgrader/database_upgrader.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/commit_history_sync.h create mode 100644 services/distributeddataservice/libs/distributeddb/syncer/include/device_manager.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/isync_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/isync_state_machine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/isync_target.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/isync_task_context.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/isyncer.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/meta_data.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/multi_ver_data_sync.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/sync_operation.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/sync_types.h create mode 100644 services/distributeddataservice/libs/distributeddb/syncer/include/syncer_factory.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/syncer_proxy.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/time_helper.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/include/value_slice_sync.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/commit_history_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/device_manager.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/meta_data.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_data_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.h create mode 100644 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_target.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_operation.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/syncer_factory.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/syncer_proxy.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/time_helper.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.h create mode 100755 services/distributeddataservice/libs/distributeddb/syncer/src/value_slice_sync.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/BUILD.gn create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/common/process_communicator_test_stub.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.cpp create mode 100644 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.h create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.cpp create mode 100755 services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.h create mode 100644 services/distributeddataservice/sa_profile/1301.xml create mode 100755 services/distributeddataservice/test/BUILD.gn create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributed_test_sysinfo.h create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributed_test_tools.h create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributeddb_data_generator.h create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributeddb_log_print.h create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributeddb_nb_test_tools.h create mode 100755 services/distributeddataservice/test/common/distributeddb/include/distributeddb_schema_test_tools.h create mode 100755 services/distributeddataservice/test/common/distributeddb/src/distributed_test_sysinfo.cpp create mode 100755 services/distributeddataservice/test/common/distributeddb/src/distributed_test_tools.cpp create mode 100755 services/distributeddataservice/test/common/distributeddb/src/distributeddb_data_generator.cpp create mode 100755 services/distributeddataservice/test/common/distributeddb/src/distributeddb_nb_test_tools.cpp create mode 100755 services/distributeddataservice/test/common/distributeddb/src/distributeddb_schema_test_tools.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/include/distributed_crud_transaction_tools.h create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/include/distributeddb_nb_cursor_testcase.h create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/include/process_communicator_test_stub.h create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributed_crud_transaction_tools.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_backup_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_batch_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_concurrency_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_create_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_snap_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_realdel_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_perf_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_backup_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_batch_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_create_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_testcase.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_enable_sync_by_closed_db_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_local_batch_crud_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_observer_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_expand_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_test.cpp create mode 100755 services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_schema_test.cpp create mode 100755 test/resource/distributeddataservice/config/trans_permission.json create mode 100755 test/resource/distributeddataservice/ohos_test.xml create mode 100755 test/resource/distributeddb/baseline.xml create mode 100755 test/resource/distributeddb/ohos_test.xml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..51c63e295 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +*.tgz filter=lfs diff=lfs merge=lfs -text +*.trp filter=lfs diff=lfs merge=lfs -text +*.apk filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.asm filter=lfs diff=lfs merge=lfs -text +*.8svn filter=lfs diff=lfs merge=lfs -text +*.9svn filter=lfs diff=lfs merge=lfs -text +*.dylib filter=lfs diff=lfs merge=lfs -text +*.exe filter=lfs diff=lfs merge=lfs -text +*.a filter=lfs diff=lfs merge=lfs -text +*.so filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs diff=lfs merge=lfs -text diff --git a/BUILD.gn b/BUILD.gn new file mode 100755 index 000000000..f85a08917 --- /dev/null +++ b/BUILD.gn @@ -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. +import("//build/ohos.gni") + +group("build_native_test") { + testonly = true + deps = [ + "frameworks/innerkitsimpl/distributeddatafwk/test:unittest", + "services/distributeddataservice/adapter/test:unittest", + "services/distributeddataservice/app/test:unittest", + "services/distributeddataservice/libs/distributeddb/test:unittest", + "services/distributeddataservice/test:moduletest", + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..4947287f7 --- /dev/null +++ b/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/README.en.md b/README.en.md deleted file mode 100644 index 5b16104b8..000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# distributeddatamgr_datamgr - -#### Description -Distributed data manager that provides the capability to store data in the databases of different devices | 分布式数据管理服务模块,提供多设备数据分布式存储能力 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 0d934e520..2d8851fc4 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,127 @@ -# distributeddatamgr_datamgr +# distributeddatamgr\_distributeddatamgr -#### 介绍 -Distributed data manager that provides the capability to store data in the databases of different devices | 分布式数据管理服务模块,提供多设备数据分布式存储能力 +- [Introduction](#section11660541593) +- [Directory Structure](#section161941989596) +- [Constraints](#section119744591305) +- [Description](#section1312121216216) +- [Repositories Involved](#section1371113476307) -#### 软件架构 -软件架构说明 +## Introduction + +Distributed Data Service \(DDS\) provides the capability to store data in the databases of different devices. DDS isolates data based on a triplet of the account, app, and database. DDS synchronizes data between trusted devices to provide users with consistent data access experience on different devices. + +DDS manages data of OpenHarmony in a distributed manner. It consists of the following parts: + +- **Service interfaces** + + DDS provides APIs for other modules to create databases, access data, and subscribe to data. Supporting the KV data model and common data types, the APIs are highly compatible and easy to use, and can be released. + +- **Service component** + + The service component manages metadata, permissions, backup and restoration, and multiple users. It also initializes the storage and synchronization components and communication adaptation layer of the distributed database. + +- **Storage component** + + The storage component provides data access, data reduction, transactions, snapshots, data combination, and conflict resolution. + +- **Synchronization component** + + The synchronization component connects the storage and communication components. It maintains data consistency between online devices by synchronizing data generated on the local device to other devices and merging data received from other devices into the local device. + +- **Communication adaptation layer** + + The communication adaptation layer invokes interfaces of the public communication layer to create and connect to communication channels, receive device online and offline messages, maintain metadata of the connected and disconnected devices, send device online and offline messages to the synchronization component, synchronize the list of devices connected to and maintained by components, and invoke interfaces to encapsulate and send data to the connected devices. -#### 安装教程 +You call APIs of DDS to create, access, and subscribe to distributed databases. The service interfaces store data to the storage component based on the capabilities provided by the service component. The storage component invokes the synchronization component to synchronize data. The synchronization component uses the communication adaptation layer to synchronize data to remote devices, which update the data in the storage component. -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +**Figure 1** How DDS works -#### 特技 +![](figures/en-us_image_0000001162536643.png) + +## Directory Structure + +``` +/foundation/distributeddatamgr/distributeddatamgr +├── interfaces # APIs +│ └── innerkits # Native APIs +├── services # Service code +│ └── distributeddataservice # DDS implementation +└── test # Test case resources +``` + +## Constraints + +- To use all functions of DDS, you need to obtain the **ohos.permission.DISTRIBUTED\_DATASYNC** permission. +- DDS supports the KV data model, but not foreign keys or triggers of the relational database. +- DDS supports the following KV data model specifications: + - For the device KV store, the maximum size of a key is 896 bytes, and that of a value is 4 MB. + - For the single KV store, the maximum size of a key is 1 KB, and that of a value is 4 MB. + - Each app can open a maximum of 16 databases simultaneously. + +- DDS cannot completely replace the database in the service sandbox for storing data, because the storage types supported by them are not completely the same. You need to determine the data to be synchronized in distributed mode and store the data in DDS. +- Currently, DDS does not allow customization of conflict resolution policies. +- DDS supports a maximum of 1000 **KvStore** API calls per second, and 10,000 per minute. It supports a maximum of 50 **KvManager** API calls per second, and 500 per minute. + +## Description + +Some basic concepts related to the DDS are as follows: + +- **KV data model** + + KV is short for key-value. The KV database is a type of NoSQL database. Data in this type of database is organized, indexed, and stored in the form of key-value pairs. + + The KV data model is suitable for storing service data that does not involve too many data or service relationships. It provides better read and write performance than the SQL database. The KV data model is widely used in distributed scenarios because it handles conflict more easily in database version compatibility and data synchronization. The distributed database is based on the KV data model and provides KV-based access interfaces. + +- **Distributed database transactions** + + Distributed database transactions include local transactions \(same as the transactions of traditional databases\) and synchronization transactions. Synchronization transactions refer to data synchronization between devices in the unit of local transaction. Synchronization of a local transaction modification either succeeds or fails on multiple devices. + +- **Distributed database consistency** + + In a distributed scenario where multiple devices are used in the same network, distributed database consistency means that data is consistent on these devices. This consistency can be classified into strong, weak, and eventual consistency. + + - **Strong consistency**: After data is inserted, deleted, or updated on a device, other devices in the same network will obtain the updated data. + - **Weak consistency**: After data is inserted, deleted, or updated on a device, other devices in the same network may or may not obtain the updated data. The time when all devices have the same data is uncertain. + - **Eventual consistency**: After data is inserted, deleted, or updated on a device, other devices in the same network may not obtain the updated data immediately. However, data on all the devices will become consistent after some time. + + Strong consistency has high requirements on distributed data management and may be used in distributed server scenarios. DDS supports only ultimate consistency because mobile devices are not always online and there is no center. + +- **Distributed database synchronization** + + After discovering and authenticating a device, the bottom-layer communication component notifies the DDS that the device goes online. After receiving the notification, DDS establishes an encrypted transmission channel to synchronize data between the two devices. + + DDS provides both manual and automatic synchronization. In manual synchronization, you can specify the list of target devices and the synchronization mode \(PULL, PUSH, or PULL\_PUSH\). In automatic synchronization, the distributed database synchronizes data \(when devices go online or data is modified\), and you are unaware of the synchronization. + +- **Single KV store** + + Data is saved locally in the unit of a single KV entry. Only one entry is saved for each key. When users modify data locally, the corresponding KV entry is modified, regardless of whether the data has been synchronized. As for synchronization, the latest modification is synchronized to remote devices. + +- **Device KV store** + + The device KV store is based on the single KV store and adds device IDs before the keys of key-value pairs. This ensures that the data generated by each device is isolated from each other and is centrally managed by the system. With the device KV store, you can query data of a specified device, but cannot modify the data synchronized from remote devices. + +- **Conflict resolution policy for the distributed database** + + A data conflict occurs when multiple devices modify the same data and commit the modification to the database. In this case, the default conflict resolution policy is used, that is, the data modification committed later takes effect. Currently, the conflict resolution policy cannot be customized. + +- **Schema-based database management and data query based on predicates** + + When creating or opening a single KV store, you can specify a schema. The database detects the value format of key-value pairs based on the schema and checks the value structure. In addition, the database provides index creation and predicate-based query functions based on the fields in the values. + +- **Distributed database backup capability** + + DDS provides the database backup capability. By setting **backup** to **true**, you can trigger daily database backup. If a distributed database is damaged, DDS deletes the database and restores the most recent data from the backup database. If no backup database is available, DDS creates one. DDS can also back up encrypted databases. + + +## Repositories Involved + +Distributed Data Management subsystem + +distributeddatamgr\_distributeddatamgr + +third\_party\_sqlite + +third\_party\_flatbuffers -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 000000000..48aabe1d9 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,127 @@ +# 分布式数据服务组件 + +- [简介](#section11660541593) +- [目录](#section161941989596) +- [约束](#section119744591305) +- [说明](#section1312121216216) +- [相关仓](#section1371113476307) + +## 简介 + +分布式数据服务(Distributed Data Service,DDS) 提供不同设备间数据库数据分布式的能力。通过结合帐号、应用和数据库三元组,分布式数据服务对数据进行隔离。在通过可信认证的设备间,分布式数据服务支持数据相互同步,为用户提供在多种终端设备上一致的数据访问体验。 + +分布式数据服务支撑OpenHarmony系统上分布式管理数据,包含五部分: + +- **服务接口** + + 分布式数据服务提供专门的数据库创建、数据访问、数据订阅等接口给内部其他部件调用,接口支持KV数据模型,支持常用的数据类型,同时确保接口的兼容性、易用性和可发布性。 + +- **服务组件** + + 服务组件负责服务内元数据管理、权限管理、备份和恢复管理以及多用户管理等、同时负责初始化底层分布式DB的存储组件、同步组件和通信适配层。 + +- **存储组件** + + 存储组件负责数据的访问、数据的缩减、事务、快照,以及数据合并和冲突解决等特性。 + +- **同步组件** + + 同步组件连结了存储组件与通信组件,其目标是保持在线设备间的数据库数据一致性,包括将本地产生的未同步数据同步给其他设备,接收来自其他设备发送过来的数据,并合并到本地设备中。 + +- **通信适配层** + + 通信适配层负责调用底层公共通信层的接口完成通信管道的创建、连接,接收设备上下线消息,维护已连接和断开设备列表的元数据,同时将设备上下线信息发送给上层同步组件,同步组件维护连接的设备列表,同步数据时根据该列表,调用通信适配层的接口将数据封装并发送给连接的设备。 + + +通过调用分布式数据服务接口实现分布式数据库创建、访问、订阅功能,服务接口通过操作服务组件提供的能力,将数据存储至存储组件,存储组件调用同步组件实现将数据同步,同步组件使用通信适配层将数据同步至远端设备,远端设备通过同步组件接收数据,并更新至本端存储组件。 + +**图 1** 数据分布式运作示意图 + + +![](figures/zh-cn_image_0000001162536643.png) + +## 目录 + +``` +/foundation/distributeddatamgr/distributeddatamgr +├── interfaces # 接口层代码 +│ └── innerkits # Native接口 +├── services # 服务层代码 +│ └── distributeddataservice # 分布式数据服务实现 +└── test # 测试用例资源 +``` + +## 约束 + +- 如需使用分布式数据服务完整功能,需要申请ohos.permission.DISTRIBUTED\_DATASYNC权限。 +- 分布式数据服务的数据模型仅支持KV数据模型,不支持外键、触发器等关系型数据库中的技术点。 +- 分布式数据服务支持的KV数据模型规格: + - 设备协同数据库,Key最大支持896Byte,Value最大支持4MB。 + - 单版本数据库,Key最大支持1KB,Value最大支持4MB。 + - 每个程序最多支持同时打开16个DB。 + +- 由于支持的存储类型不完全相同等原因,分布式数据服务无法完全代替业务沙箱内数据库数据的存储功能,开发人员需要确定要做分布式同步的数据,把这些数据保存到分布式数据服务中。 +- 分布式数据服务当前不支持自定义冲突解决策略。 +- 分布式数据服务当前流控机制针对KvStore的接口1秒最大访问1000次,1分钟最大访问10000次。KvManager的接口1秒最大访问50次,1分钟最大访问500次。 + +## 说明 + +分布式数据服务几个基本概念: + +- **KV数据模型** + + “KV数据模型”是“Key-Value数据模型”的简称,“Key-Value”即“键-值”。它是一种NoSQL类型数据库,其数据以键值对的形式进行组织、索引和存储。 + + KV数据模型适合不涉及过多数据关系和业务关系的业务数据存储,比SQL数据库存储拥有更好的读写性能,同时因在分布式场景中降低了数据库版本兼容和数据同步过程中冲突解决的复杂度而被广泛使用。分布式数据库也是基于KV数据模型,对外提供KV类型的访问接口。 + +- **分布式数据库事务性** + + 分布式数据库事务支持本地事务(和传统数据库的事务概念一致)和同步事务,同步事务是指在设备之间同步数据时,是以本地事务为单位进行同步,一次本地事务的修改要么都同步成功,要么都同步失败。 + +- **分布式数据库一致性** + + 在分布式场景中一般会涉及多个设备,组网内设备之间看到的数据是否一致称为分布式数据库的一致性。分布式数据库一致性可以分为**强一致性**、**弱一致性**和**最终一致性**。 + + - **强一致性**:是指某一设备成功增、删、改数据后,组网内设备对该数据的读取操作都将得到更新后的值。 + - **弱一致性**:是指某一设备成功增、删、改数据后,组网内设备可能能读取到本次更新数据,也可能读取不到,不能保证在多长时间后每个设备的数据一定是一致的。 + - **最终一致性**:是指某一设备成功增、删、改数据后,组网内设备可能读取不到本次更新数据,但在某个时间窗口之后组网内设备的数据能够达到一致状态。 + + 强一致性对分布式数据的管理要求非常高,在服务器的分布式场景可能会遇到。因为移动终端设备的不常在线、以及无中心的特性,分布式数据服务不支持强一致,只支持最终一致性。 + +- **分布式数据库同步** + + 底层通信组件完成设备发现和认证,会通知分布式数据服务设备上线。收到设备上线的消息后分布式数据服务可以在两个设备之间建立加密的数据传输通道,利用该通道在两个设备之间进行数据同步。 + + 分布式数据服务提供了两种同步模式:**手动同步**和**自动同步模式**。**手动同步模式**支持指定同步的设备列表和同步模式(PULL、PUSH和PULL\_PUSH三种同步模式)。**自动同步模式**由分布式数据库来完成数据同步(同步时机包括设备上线、修改数据等),业务不感知同步操作。 + +- **单版本分布式数据库** + + 单版本是指数据在本地保存是以单个KV条目为单位的方式保存,对每个Key最多只保存一个条目项,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。同步也以此为基础,按照它在本地被写入或更改的顺序将当前最新一次修改逐条同步至远端设备。 + +- **设备协同分布式数据库** + + 设备协同分布式数据库建立在单版本分布式数据库之上,对于存入的KV数据中的Key前面拼接了本设备的DeviceID标识符,这样能保证每个设备产生的数据严格隔离,底层按照设备的维度管理这些数据,设备协同分布式数据库支持以设备的维度查询分布式数据,但是不支持修改远端设备同步过来的数据。 + +- **分布式数据库冲突解决策略** + + 分布式数据库多设备提交冲突场景,在给提交冲突做合并的过程中,如果多个设备同时修改了同一数据,则称这种场景为数据冲突。数据冲突采用默认冲突解决策略,基于提交时间戳,取时间戳较大的提交数据,当前不支持定制冲突解决策略。 + +- **数据库Schema化管理与谓词查询** + + 单版本数据库支持在创建和打开数据库时指定Schema,数据库根据Schema定义感知KV记录的Value格式,以实现对Value值结构的检查,并基于Value中的字段实现索引建立和支持谓词查询。 + +- **分布式数据库备份能力** + + 提供分布式数据库备份能力,业务通过设置backup属性为true,可以触发分布式数据服务每日备份。当分布式数据库发生损坏,分布式数据服务会删除损坏数据库,并且从备份数据库中恢复上次备份的数据。如果不存在备份数据库,则创建一个新的数据库。同时支持加密数据库的备份能力。 + + +## 相关仓 + +分布式数据管理子系统 + +distributeddatamgr\_distributeddatamgr + +third\_party\_sqlite + +third\_party\_flatbuffers + diff --git a/figures/en-us_image_0000001162536643.png b/figures/en-us_image_0000001162536643.png new file mode 100644 index 0000000000000000000000000000000000000000..05c1fe6f34953433a56cdd4fb5a5100b9eb990c9 GIT binary patch literal 79823 zcmeFZXFQx;7e2}(Ax$I^L9|pvFVSnN=%S3M6TK6?#30dvAklk?=teg>(W4XHV1h8r z=)DbR4|(4A{d7K@-|u`m|MTI=bGz?7``%@(wXb!pweA2V1u4=?w=WS85Rgj0dag`B za1KsD@K5XoVsOXppHUU?KSFzDsiy?_J+zD9k8>tZC(@isJiGb)sh_1sl}eue_O#|ddsTyUpVVbZj|R?`5o)4Sn@5j zMBXTUVhp;befh%0YfD)vcNp(Jaa(;MbBS;3)>F6DYsweTRVSU`L6Fl&m*6-kh>+V*K_IeiPi4tY@~_YZIus zyM#bm$3RHG#6nix&>~GANa$lkPdg+Iw3BvyO~eDgD!h=gbb_RZ;eVF`{q9}AQ^pqh zqco?!t5kS)Yv9>l6GIsK9C-Lc^6Q@$q(4g!P$xK)Wf}>C3%Ch>5+UGjCiU%unVM&; z8Q)?vdDE~zgF+HH{vpwe%I`JIa;)HTK`;i=eItVcTsqu1y~BJ(F4y?t0Dc37FP=WQ zNt~fp;$w#2oWygd4=xc6cSk$2~k7$Ai-cN>u;f8%|%^LY$?PdA!2rMs&SsZf-#V zXJy@$15HPyIMit|y`73+f&OgPlFpSl1k!2##iNMobOiP>1SNjB6s`@u3FF}Gx<5rs zkVBe5AsISDf)Ztvi}^BvP;o$(29lQ)liw#;U^v^hl5Hu^Q&YZEsIqfWKSBt>ehCdAEPmwlvE6s&H$evwm3axK-_+j3o4o=jrzPW&9VkouS2q7gF=nwO`u3O8ac828482|*SZ9D$gOr?<5w zGb#VYLL(WTygb4E#VE5O0WY8Dj?G9y$^HKb*qw|>csXrxL2^w;JfzM&dAG$inte6O zY!11!K3C34LXbmrwo29=rN4NzY%T^V-kRCF33-_;YdF&&#t(PDHyci=W&JWj(T+Ht zYfpImYjhrgWaYmvXhBOq8p!FLbBxN}Pw1T)?=Mfga;Uzy-58~ngt3=6Y5C4x?*6WD z-&_tA!Vm1!Aj#R{OdJ>P)jVM2Pr8z<{e3uS5OGj%Wzbm@V0Zt)RMhE;#2s{x!+01rjq> zgeh^r_H8kH^mR8~xz|J^%>9Vy=Z$mMqvu9;E6;t?DgF~4AvnEW${T+mtLcu*esGDP z)*dhkaPLWEKJ4J zoO|}Y>ziuAn)EYsm)VXnZ?yC`t#miM1b*DrO!iM5`wvpXU5tg`dBWv?s<5l*`3Y=q8D4OuzINXT zj}w^D;a3-Jv&@~ zslF0-av3|!VG~?F=P?%h?PJE!%(Z+QzG=kdvzUEj&}`wH^RfJekz?u63U_mt-wnY5 z+!a(nztipHbEO*Z+lT(_!MI>-%$H%P zmznP+^%1#Ts6<8DdHl`WQBfYG11t=3tN?gsUDMM{y>W&-w=1~d;~!2zD8)VSijQ1y z&~Yj~Wrmu_+2vEvQ07_&1$f=yY-6c8$?2u)CoZ_s#l}+n7~QmEAj>~aFE3rspkR3t z0|9rHLN5>G(4Af01q~lw7`TKVo~@o{8PVw_%kvmW);T>*JQx<^kjr&?dwS^$8b;nZ z8)=)b+0fPhBL;1rNzH3)IHDgYwmZR_VBaGoa_=N7)zYA9ql2}CD#&xVB#A=&csERdIGE>rd^q3K;JXvEK+EcLik z?x97J=4L3IogXnC3ke&}rVW3{;ZeWUoggPTlA$P0Zf_Rj;FE^i~5}G z4yh(aNE6^o^B{rYndaxp-|lY(Cq`+>O$j(%;FI~Q8H2FJ+gmT?^Hjz8N3E*9vTEu^ zGsO=gc!wj%b0%4!*zE%G!|MI(<~3(ptRyn_8_6M5NI0nvmh$@(?M|N?H*`)k=j$V} zicv13yrgX0(y=P88IhP0g$^b<+jy&QH<^TFK90xgUEq89pO>}>sjzjD!n%|Yk3`f< zCpe=JMRrF{+5E^vHQjN@GJ{<8^_;T*zS82QybOPe7HH0Gwl*xFe6S;pC4G2~sB%)o zG053nJzCo6Kv=(2S32O4b;(1a9F>dVp)W3!{cBhzqw}BG3^o@}^tD|v`YWNLhNAnz zDprUCLsdQHyAk*Q-9G-kxYyxfqzkurBdnCeE`GOZ$ZP8JLRlA<2L#R3E@@TnF};_u zjJVP$G4bq9Y@U=IcR?m#_vHZA{< z@ldwAN~iB}fg4xhHM_qCZ`sh>#{4b+ShdSd{1^LM1u+^>T9_c(Y{s zx#x4uhv_h-; zZ_5{7=9^ZcB#n$lJ=$NDBonzUUS(zQSDO86v;UfuH>9L>PVem#M8V!lcLIarp*8t6 zvOM_FDq);1B&6laNU8EYebnfq>%Z)P3vwiSEfu)-r)Nvn;V-_dgburMLH~jJbvk9R z8cW)*jj)@N6!DO-o&G4ZS~1Nc2-6CUK_vrfB2yVki!2P@SrIkV&F9R84pDCEO5R+ zjovx-mb#h#2lR+h0W1>>33^&lH{?U3HF0#3@=H|gNUxQ*vB26zVduo*3V~|;aM}A# z-lr8hBev#ZgMRTz3Gyof9LXO6Hd3j~7)qP&7czJnGu1(Da^!kY<9}&=YYj`2MCai- z05v>OY$K2?Gt;Rq7ZdT(N(P9pood1LPm6Y_S!9zpoTx%NRNB1V&u`s<=#brwR zu^oRy{F-dXdr-F~yS#MSM%8jpxb_|urfv&SkZeykkPN?=wJyT@yhd>lGVwzGY6~vM zq-WbMhFTX$K8p)#yX}`2+HMx$XQnd@_he(!D!N-^-%I9)BNKfPe_4F3FRlGe7Rh2! zxjT!g=97ZL<5d4fc=^=ropFnTLfa<4!j*=V>Oul8!Qp%ja@~`I5q~Dp>5F<3U#0xy zX$0)ppGo22JK#1`qFqWIah{8^&`ao~_tsGY8Tte%RhCnwj}qSZ2u-&udf2(l^CnZT z#0&9?mgY}zHm1bB)ol3k)yxqIFZUz!TzIC}jTw@2Aawi89xvce?y(WRmg#wrsljlP zqh8B0@qV>dujydJLzsA*l3vZ>WG}yS#?t%ZVSTg zq9hKBc`3;7}ALaF=i ziOURmas{u6d%LXe4eQE%<9rilS~*rK9v=8g)n-p2;C20mav^{>*t$k$Gj@YfA*OJX zgEiTUO}o$wOD+7DAaz}E7o7QYzmYmKIce@oHWfh@W0mpY!GTjdwKLMH{9WHdzo_Ov zmq-RK`F$2Hs9vpNb)UWr{b@~^Uh>ZZslzgdW(8NxHoMK(!+ow+TBkQV6CVc=1m6FS z*FoHUmIn0$uw>j@f&l}HBJ85uy(jL`?yr}M+UWIK+hf5eOKa+7I*2tm=6e zR*wr##viJ0xi4ElJ7+3B5C;B(A9gGR?2B`Zh{@H97bMA2e4aYLtXpv`Kt7!QB(&A{ ztfU(LO7d;R87uR}zlTIjU_FN6_zR~nq1(AhBggUVOh1Xk5((r(#Vt{+MsEi_b(8kB zf4fdT7|g_P%DG7~$EixRA7%Ysi{9>}bFa(sW->$4Mnt9$W$nU?F*Ie}n(uVts=GMtL7I`_oR9|N~`hx)MnCJ zC-mr^7GsQ(Wab&SeTS@;;LqM{$Ylw!>2oVH=NIle)M|R{p*$uh(a zFL!DQJsf#_6VT~bsO8N~P@bNGzrBbWVZ0{Bi4Yb*-~{DsiAiqOA7kr_N4{q26Zqjh zTtK_#S!T4xI~Dx1dnOt9l(ONz)xJrY!P^#`KfV`N^sQfQG3 z3a~t9b2k0>zI8Fvf7SgEska=**B*1T1FgIt_cfYVb7h3XWUDfry=0kKrh0=A1LlAB zQkiSFx{?`=S5J;PwLK>t$9^OR2KAR)I8WJ1RV=pBginYXyxf5_^5v?pvZ-8b$cbbv z*>>|7#;E0nNq>xD*QgN;s`ebtE6gvGo11V%Csq&khlp6$C)yCCa{up=GFF%*!<+<< z)|yC+G#@d^%~?F19T9UzCDuks^iW6kJ~D3EmUqo3x+`UlRkJ&#Q%AvDx`fy1nH{7}{Srl9iE;NO&s}HFa@qZ5 zm;FvCeH4erMup8X0p^xFH(UUTfRX z*S7Gat?ykGGLd9TQe5y98oWYLhnQ_Eo@^y1 zaTc07upzZ+jb1m1%Ru~8M42=+OxLC1eD$Dh7nx@aOfN@x>(yuA{jO+BxNiJey6S27 z()3acq>mJT2E~{;{vaf|GFDpkXC|o0#&a{Ct$}MRMM3O0&gOXDrqQ~3If`0Fc-2o| z(=2v8n+JZ>hOp{f8FXvqO}ego9H?$s9(sa_uAitpo@UpgSLcw_0s8w5e*$yC?~uvA z>lH&Mtj%$Hy1vR7DzDq`UJGiYN5&Qebi>K{r+lYg|| z>u;!;XumTi9sa6w2kDGrGak%(t?`jdb$~^IF`{B2)9+;U1XsUS3?f_;?{M2h<|2}G zixFn&Sh@Dj`|xwRvp!0$GRK=9{Hcz&kPHVqTI3}1*Oaj;SswCk%8t)m7i_>mM60Ly zgdWpYP>IQT_w%m5VK1Y6i;MmM=2H`y;=D)4v_oc{zW81PnQ?FDFZ*n;YAr%(<0&H9 z$I}eQcUe!+>e>y@>23$NZcWEQs(J<)EUmhC-KR44p}IX;E)2-4WwJtP$MSH+)CBIK zdP%-M6yOZ}e1vJmer)_MfU#@4O#2dHDoVR0<#T&OslK-SbO$j`W8mT)NdiU#3yWZ49rAF~|dB0;;H8X`s4n^*s=y5qknn~EtKL=C0b-NYY z8q^|~EqC~kz#_VZN`3N$D=_W$c++l}F*KxZvUxF>+ScP1uhM|b9HyTvaQ_F1I55yk zcWw{(8)z)tSr%-~xj}Td;%YaPy1oU`@atz@YMuuDJ zvu-)=FKRQESZo5wLbS@*&s=kH{MU9)@;=M7CxCFVViAy$)zJiMqB8%Ub z#!IlTk+9#mRn$WSoy2~-VsWzX5w`x=!VVUQ7F%^4YjRNur(pgY5ZvGzFh{|5>B7vm zgVRFBkHaOSdCjkcOKhK&oUzBquo4o&0&}3a!ryQAv@=@ZFp5r4A=nj@a@qu>evd>GdSG*9QP%3-O6vp>o4%Y>=sxA z(?ItcDg6vJ=hkN^>jmBaG%@tlJub3+CBf~sjMeoA2uBa-{bF1K>;yL^f3b=&RCwbj zIJLE5ALlHpUNTJ82vxs_I_u%~lTTbL=H}x|<{IU4Yj>wac%vPx#u1P5tPk^e4D}b; zzq6oY3_{xHJ+rOwF|LL${Vi}pU1!-u*H&txszhbHqV(3h&dfy7)8K)ele!vzk(Hy} zxB^}9=N!tK-{+_CmRD;)#wg1YNbPYyAMcxi4r1p?jdY9UTLJ4!o&~^qTzv4wB|)Un`z4A~rA$LKe^EfsxUc!peTW104ov#8?FXR{ybJ|7*SC6XtHzTD;&#mv^4 zZ3ig2-%D_ukFEam8J+B`Te0p(Q{0{jBfXt1ugc+z5mPVia=JNZCj;wdS`J+ulLxh;tiYafI*qV+Lpk<#HL0*ZeicvftpN zxm<>W8b255MU3TQ9-Ap|e86yzx5h&#p$@P2x(hrLtt@^X@t`LWDkcy8JwnBHey%LV z49Ns;%CU-xNEu<{McIQy_#%DH&KD-8#zEq)EnH<4YcucQ8`+AlSU`WQO4JJQ6MsJ- zQ@|X(L@~qd>L-|?X7lgnchqksG=!ENcm!Vr){s0NauBA9lTP5xLG6;i#X;v*_j{a; zk&7?r9}UPLF`0h1JAoV$x4xBU)p;+gDKwh|oIoei#hxQ4()t z?(@83Ww{A_k-Rz3SKI6?lmj`=;1s=^cJ)cdPz)gH!&Dq^WDABO)-%+mkVFQ98jGZc zRq(Cep7DHgNL*}Kus81Y znApzpc{=NxntZbO1mgsE9gsm)$tOL2bOd~a@C4^6*Xjr>X2OQj9Pxug15G}u7|0j@ z)~f(qn;t01<<^vww7#iawFqV(U-=WRs1QQhA;F}O5Yytm;CamSo>>3PHulGeQw_+B zEKNF3QR}V;s{1ej>pNERYKU^wO~<)PTtz4?|215_CKl2m5_-5ET0*(?-L6$dQRv}V z{aej#eql~ko44FZFu#*f&0m z)()$A_6!NUX-m9?tVeFIIW>2kG9_-6@Xt-G^7k<+ExPRE|H&OenVK8GS00 z_L(JqH5Rd9Ma!OnGtyNIN2aA(9!m z@4XNCK;O(Za=bO5NOrOpdJ+R4fTv~Kbe+qf_`yJ(W9Ha07i}^!E`r+cS<3tvMY|9; zusq>5?^XAj+|9R0zeUfVE^25|KKDA@Vt?xWO8fZNnU(5Q4{aKs8`tD=G5>m0x0$wF zNMHe}CUm#VG_&(7?dJ`lZl=iHdSJpT(m4gTD*`$>Y9H6U?-eU?Ty<#^DK2VmRDNGSn193-z{p2+bDoMLMrFkgRr5`zFECvC9&u;~mS|neNhGllyVi-7(zv zV|g(xIy=ml$bfRCZQ@;wE@H=fs6$=Bm5NznBTd{S3a8_wkZ60b=q+Q;cXx=69H`Taq7WX_-^AoO%*P=_D(p{fGi*ddWCneW0WqAg+ z#p;n>pX2T>{JE};nGER>PlzupC?k0%gid6~HY4(L9v)kc72k9XzX_s=IS0%(z-8}XMJfP zpL3<`3AAe=Q-1uVF=YnDV`?B8eDJN7KeGd_CB9v|CX#Tk z3oTf+b~l4Afe>L9S^cXbTYY(57Mb#$!gn0yvaNi>zVC0&``!-`yysp%M2K+}&F&#j zxwznVJI^fL;uT6CheMsfTsl;e!X~-LIBdVB!Z6vH^KpUZ1a(*<>&}YI&pj=53qdGDJTDh3j!HPIztFm}3<8puCfCh*;hsRBS#b zk2E`(j_`3n1Aw{4e7lDLkt7C_qRe#sm+km;pm7ZIvG}*)`L;qO9cpL~e{CoyO0ON094w8FP1Bwp5rtYlfNFiC!Dnd_B zKwzWv33pVv{+lNQ{f=C~W-+hi@%By}n?^;~vhjMB$&G~Tcps=ImrbmchBXA6oqu}j zcTmfVJC+XsyH1cr{EQDCA<3p$%KTp8Q>#B7u1sgp*mT|aC!WgmX^KhDqCCg@r6Q?Y znKq-qOIT|n>useTN|s{bZY;%+I&gr+KDDt2OG`t$-jCq~DZ+StD$B=u*28wYTm3!V zW@$=9=%P8HNs*~MAWny^v^_cMJxOSRZQW;Rx0A8aTrTMqAEP@OXN=?w91H@1csCkc zEjJ!jd+#5Eh;DD$J)!dpVgR09B;DEVPM{XA+?^WZojIQ2Jig}+k~FVM1=B?1uFSGR z_hno<<53C044S)(KqB8CN8TW@(OkS+A#v0|)>vM*4cF$VUMglYTi-U&+$Qdz0l46F zmC4m7?*fLVui8|OAp<2#JV0 zE$v@pA)cE8p5ZOp_T79j5Q=kW1d-Fi)c`!q9@DA7Q;T@mm|7#RZMWVseL4BW-DQpy zTFKi>?l%1)3FbIYZQ?W68MWNCXsEf^slfGummhvVF#A2qZGdMCzW|T#X@J)a%7;X5i4{p|DFQ7M5*83+mPaoG{1xdRYxG~f zepBGut$De3hCYkibAhjMavyU@&KQy9zFw!}x#Sa}azzp^bNuuds!$Uh7q{gMfITsQ z3MCv5nM~eIx0|iTZnwTprbIm3H^RiWYZ_OgC8yqA8X^Q%_B`OxYz8h4bhu>k?G-@F ztxqA~kR%6Qfv zkT0fZfYQ{TvC?hov&S&dt-kkw#1a7TUhi(2`Al=b^p|H>+ZpR0+#$ff=U-}Qx$nU) z3;@T9xhSpiV;_)|2N=s!e(mw`ZksRzqhb>6Rk5wUr6?OQOV`n?fN2DEADqvZ|`t?_;9`t&_bu9cw#@2=$5fsnR1yd8`k|YC8HJ))JZt?S{zr=*N z(Vr=XTbzER=qj@}Ab}fL!z+yFdVpfr@yQ=12_X^nQ!|K*L>Nl% z72r=7>XW73W42BTJiE^ZEv4NM>UCA9n2)o-%Orsu>jE*@B<2WwnBdOQAyEEA@<&O# zHE3UpOvPU7Lxo5}0=)429EHmHZ{j~z0P`P8BKt-t^8qNi2EA#70k<5`Jg zgXrdG3mmzfoYicWspLcp7ym0ZpZW#u@wkULvX{E$5Ov~_^x;z9v%h`ZO-(*d^cXiA z+q~euu`a=I^mgYKRT!Dq6j<4RSz)RUNw*S34FImGb&JxS{x$!D~FYW#*^kdp&$j5K9N*-lWGEH!`8fW zJ^}aZS)YFiG%g}Te-@`~_cd@}2LTP#s5MvH0|dY=5LU?a%~r|ugW@)#MGR&Q`H%$= z^n54p4DQCbI0b8oWy)bX|3QV*;oPih9Y(q(V@GybvBlhT7BQcwB`o2d7qTh2%eosU zy)^PRKeh&UyNfRkw_*E2hxf(yc~?*GRBhg;b7VQ3%zJVDeD7Dqc71k%i>CpFwpcLQ z)OS=FZ|qobjdZ;|{D(mx27hbDK37gv0#Hq=2*ARbbHUuE94U7fy(?D&7Y2~aBX2iS z?zQHqCX0W;DSxJ~%10-I1IVUZTmoT*>=|uXQm1Ikae`Ha4%yN#c#sTq?04G zKd0w@=a^d%4lrRWzt(fv-JCvf_6i>6l{lH@hKwkNNQhIv2bN@Iqg+^IAt7(A>**&SQADAPSfQA6ld8qpz)+z zG1inw&O`xxj7^Hx!-WUp$sl5)IuD!{`Bqr<_Aa8zcie7UP5q&PqGh7gI-h+TB@HSXpe3ML1!eT$dKjG2(*sa<;K?gbN)?9p(o!{5jp z*q|W`#d|gE=-!+4Wk`K^pqhjcI%>Swbh^=SVj5nbdK`s-=A6t^^M(}(3*^bABD4Xu z-9F0OD`*gnz_=v7YrNP#3P|GR8@cv48o0eK)}i}&HS6{CDSItYw;s%e)Zra303MBK zz9@ITWA`HULrBIB7O>5-t@Nm>s9B5orIKES)+A5h{jR9zvSVbGiv%WJW;xJ#2aG5Z zR}i(S@8q+G=Jv|Az_9VC35_Hrtqg5@c^;=3pNpZ@##`pQs!I>y;*+b<;~ThDPYLSx zAbz}J*KNus-4gIHh;r4g8y~P~7YsJLrOtnfn$n8pS%XLLJmnwP0)Nj#tip(-u1BUt z8`QZ9p=^7vXVsxQW5yx4&0y#uB{bo)8fqGuQ}+TU#{z+cg;HRum5VS~_vHLKE(%Jt za}E?{2_6(57ze3o4LWD)4~-lm%x4Z1ZQKo9I<=<4g71q%3_h)@@U=E``?WHnGA)*5 zTFmm059=+;UKr@JKnzSpA!d2u6G?uHi$=Zji=FPKUh}ik#$(Mhw z>Jy7Hnd{ge$Mt)dtr%hJ$2urZ))`K?mjG3!=WcP83d$Qdu)lGon-J%bOrR)k5iNmpK zZ7TC1+-v=*{SfD+4S_G!IU)Bca>+T4--`iyTA{UrUHx7v)$$3vd?M^Kc@QQ0xa)PZ zdI!DC8Qr59N)Qz^trstwMXXVY{g^xi zZDrd=MRTxcxqV39wIwtQR*9 z(*?fP{YmGPZ>0w?RAlvsn#kjJxt0KY+}A`R*C4Oml0`wnc?ZR|SV1T5WXZEbTHuM} zK)rjkqXVM%JWe2rgrDQBmf^DP~;0i-?C zY97g>ed~pq&5^`>LY>RmZ*)Ye{MZx#(W$TyS1PV^m}S9Fk!c~?v-&caHWshhZ7kdD z7z3cu;qG|{Z0}W{eLfJTQ6Idu?VBw)E&pyCjxkWN#f0OuzLh~S)ha;qaVW_)Y*_)! ziU+85@9N{$?=wCE6_1V0fLaIWY*Y{14#dO+4p7|=Rl9AWQgk~W=dnm-aRnUbUAy~< zb|1k6!ZjnaweM48PBiaGGdsV&3T$r=Ioi1+Sh=Vp{m3Jmy`vIb^)IC8#m8ISsw~4~3r|tj=`+u3psaQ~ z79a!?YLR>6n`)jwBO3%ooT0DI2Cb9L`-$ZAWcmdDwOR$JD+I~{Sq?}2Z0{xK~Q z=E#0yLvVo(4HgsSWlDp?Dcfe_FwK0klb&hhBXTuR@5Rf)w%y+kK2{1HQb2hV7cK`@ z850jtUYU*SFN+Oi$HWC3esmi7yn4B`8xPv*QnDCkR=x6b z)h`w`x3#F&wffj8eTH}-b63?PTCn`*Dse1|bS-Z2IaJB_h@;M!B@snB6Sp{bWW9Xn z;3I%duCRu81+mu3Lck>@XxI46&GbGC4_4)IPl(Cm1IcY^57j0-z&g-I(`e~y1&tOo zlt$w8>&IJKZNXN|R49}>RvboxhT4%Q#4nbk>jDO-lFXgI;&~{LLKgRPA>;#8bmxte zN85-hMSk^k^VJOa zBZN*^su?bC$@ZZIwH3tvpIHxu%qX%F06gz@=(HHJmIh@Z3u=b+3+dFiJ2R`$QIUke zQwIef>Gl>~AtqsaXnc+BkG{Umn${3H8r}U4bw*FbBq@uL=AzfaZhj6nr9bVmz)#R9 z&Z~>4_U_ABizEK_Q$L~OF?{QqMy-y*_CBTSX)j7sJHDLmcWJa#dgZaeVFNe$K#Er6 zX<@3@R0|C#fdKvyxGfAgS(dX3RzNJPNt{+jInawGdzoma@-*|}+qhBMfYObJAWI9( zL6j%k!GGw}_4|zg-myE{6%^49sHTB$c)IV4n-W%6Iq3+b*9l#gu_S};&p7kS*yxqiVqInT# z`9Z4&-_PF?Qv6J%9|8sr%z*8H=T$EO%OQs`{>7F9GEfJn*&0yGoyB@vCD(!c^+Wkw zkiKV9pDqUnyXMu9YP&sQ-*fSlFX{%>66LVWnLvWG%~#%UWAJo$((O&;^{?InZ}bbe zl1tGU?f>F3CF6**H-H6hqa(hQ+Sjb>m-%&duHYyCxKhG)rrDA$ncvZTXAPsOef_WS zh9Y`0o5&=`p)HgA9b6paxz zQ|7NzNaiOUxI~drET3yT+iJAkZ9Wu)Uu!PWnkPPHkw4-gB*FALraIt9^1wm=R~A4f z9>f2QBqT{8tVzjRLwj3C0Jrg0Gqv%r%*JqQz(nzt({J}&ye|x}d=P-+L&;CEU^7fF z#dV?VcPC?Q%ibOzDWi%r{v#yu875sZUlT{vr1S9GLp^78Un-=l)lM$=obVmlXts-- zE)AC(@i8pGCA5GhKYo9)O&7`Uc$|0cOkuWvjF;(wogQ-y#uwLfxkoVdBnxm(xVF)a z3C0ILikFFn_}#Dw1BFvwQ}{GXAe*NGa(`CFnKyIT(OV|&v|!t{YTfr5zE9vy+Q)%( zJ^}&^pszr=H_@9CH^(=Cj(GzdBRkmZ)7^=OgFNWTswUcgEwA4?p` z3*WMe#q>S7M+lx!iSv+oIQ^2I#^(t&l?`NW?nr_XDxjlF1NgASm@5n*pc*kCz;9zr z*@iarIO>l!e$_8@rP*Z+;j_5>3!#8cOlKS-aNXM8HbQ&SN9(wmNg=ooZXj0xdE5SG zLA)I0q*d>!;$?k*^X9I;-*!bhqS}lG!D7=Xt63A8K%%D*9t-Jf@L&vsqQLE2I za{BV%eyNpF`>kDp^9#4|%K7p$)z`$d8ZoX9SAWjD2r90^Lkzk?Ra(hdP$%m0NsybW zga00dNzD-m3~P%zP(GyD*#)6S7H>1(4p3wNqi^HKG0;jo9KO{T5eV5_&{=~S(tBrn zdW*9MBW$|003?Ho8AkL<)JK5buef`($RM1XzU7Um1E>yLpa7}&`09HqO?ts=12QgY z-V;T;fCtF|0VlE;h|-G#rw8=eHo5M_DS}%0C(d^9s&vZ>$kz~0~fkf3{tK`UyDAEF)n=S&rhv7@3x>vK`K;=)pwG#(=o*cgboS1OO50{H zlZ1=xEDO@VWT7mjiX@MQgBPuF-vHdy##6Mmg%8 z?m8ZKw%R|Dwt5`a(fP?I(7ve@A4cpRVn7^r#0jCzjQ~a9Wg~5xG(PNDWWO#0HhS#% zz-iy$Hx`3j7ob3Nt$HsPgbvjsx)^A*h?Q>f*G<`NcI(XrQlvB9!Dp(+W()x;%=i2z z+628FH?r%c4I zRgqC+$9BbF z{sl%8ZP7^(_!4>n259|@y&gLyO@VWr+;Dvd$v^Y8xMdr%HU?*H7njZ`L%hZ;Q$0WW z{b!vhi}V%j*(jrKQe*VjMq>43xeM&Fq@S~QG;|<-3|#Eg*CElIO=Qj4%`G;Z2j2** zbn+PXtTb{+#fPH~n_Hr>(MZ(Y$hV&cQEUaZFp(YEJ)a9H@unZ^K{ETVaD4s|{E#Xoo}Wq2EYt^TZFG_WAebAjZ1+OGx9+&2c1p9Ed_8Ya|cc zCu|WM1Kl1~YryulQ+e?$BDp_ZVO(FJY2>GuNO9fh;AP9NZ%~+H?g_iBhzdbqp)du~ zy~d70!LPSJA(t&IIa)4yGpQ-$kly1ESaosBt96w&$nmJ}3cLPQ9g49bRdNR2P&(i^ zyjXq-_=s|Go^ZKaPuqtKx+&_?TPLLNoM2CADJgeU=XRE+m$+&3Ew}d(mbcdhT2rmGFfU(M;{`_Y}XEv zP@t(Hq>>t=C7$_}2be<3#b3%ydw7)_>n*JU`NIIv)tXbZQmFdX@w-O#GTk)k+}t3W zw*RDRO^$}?r&Jn1BZBtl>a<}HZS&+j`cAsdjmo;`)E*KCNZqX$%U03QeS^212X;I6 zL+IpI7Rt+LU+*C_sOu(17*mx!jhmIE9WUHqya8* z$eb}C$3io+cnE_Rz9G09DF@S z`7}GGI6K1ZVUrA*{s}(la+|bjJ9Ga; zeCYCS4`(z&qqfWj!1=1jf-Jo)V66>7BV% zj3%2=pjngZ5)Ni6oW5feTSy-`ZwZClIHN+G4%~mDWuvg9e9lWjA>KQWI2n7|-Nn(- znH!aRHeaJz4-MwZ*ALMn>6m-YKE>BZ+mJiK~X_vZ;H(AyOjyNk>@azk#?8BFIiS z;A`~}YSfs%hKzue6;}`{CB3r_atGY*ua5Isgkb~mp=f|=Z%hgo5T*#8{0#r`OHOF8x zLw*eG6=){;H-!y=SxKhsgtM^`>6@{kv^&4nH&i^(|-4uWKzhm6mGq8+$3i zPg6Ee9mqjiYKR-h&)vGiNiOG}1m$4XzlWCJ5CFvB1TRjP<<1A~{Q~N){VJH+@>Md2 zokp|%R4v(j#3S**sbYSR+zytl^K}eLBoM*E^C%L24eJ8+mGz01^A^U?+kj%(tXRloy`|Sm-vyBA0v90#a}l zpKyGdWcvI81@L|eYEO9z#F-Gxekilxoy{>tnQ(zq^+wfXbyqT26#_|ayo+=?-9Bj+ zv(rywq5H~>tKMrE2uhGWf01+Q-|MoZ*ndtY0p8ePIB>$;R*2h4z>iJ(nvXHL`Ii`Q z3sOy!&Xa1G-c^u}lg=a0TKo1l2)6X}U_nL6+8rmbNLTMjWOND0?@9r&g<0^M>?{>J zwBPuQ-@dDNHs3bxt&hDCckGBzCcpl7+0er=+y=!5_^LhGVij93TrMv9u_`_qt=&?= z%lVl$HW=O+4ZcSZc>0|%yqcZb4g!KLj;TA1r82;D0Keb$uk`|o2yzW>~p-{r^8+xk>{K*`cdKC^MAVmaMWJA%yJ2u}8xwMA@=ZM#i!CIIheP zs${Y`5HJ44FDA-#KI`Ddl+N=n|qs6A%C`ks~-4O91y8u8p?ZYyY-X(`gR z-#$CW-@>jq?Yq-y&rdqJ0G)lG!!#-cN>{xv-@hc1yNPnRJY0{O{uJ9pwMq81vg6Wv zzf=UXLXn$hM>aMpOToZ2#PU>1JoHEBMkWe^lhN?*d>1dI#~?&b)eI-qaBG}=r$v8q z;VFdo2HCmFhIK7-SaiG1=$G-1L;Ehw^e%sWzANuE8ryzAyJYD+idj|WIrm(qRv$G*6Dp+HzZ@{`(eNiZ_4~Sf?rw|9pH$>J;g+DFmT+;ZbglyE zY^aXzSGHQw8-MRYKhPeG%3vxciseo1Dr@pWSD!ikwkHzeLvJ&5)z+m1l+;jaxDd)EoJ zc}3Dmok-Ws-22YBNN*tYQDc;R!dtca?!|uh&CG7gl9|Q7j*FgLT>Oiug&s#kV`3l^ zEA(e!L5N?JZSVI-^_06723u&k)iWC9qFYvU$#bn5)Y2(AbYms=?JD!BVUrK`5KAq|Z_>1Q zg5SEYeXkWtzSzW~(Bxa_qsjHgT@nrGL&DyvMpox~a|L!PN{kZ4uZx8x=I<8{rqc|R z!5&KrSSu`_y)1vncyWN%S3Ru}%c&5(iN3rhY+v_w`jgt8L{|moLr-+BH*<&cS;+i( z0PsFsvS>bx+w@%ZfU;C1T~o1v_C_Q_ioFuX%shXr27enIWT^C;pvL8K zBK4X~p6H(wVx$9*1|N( z!jOJau(jP_YlVmvXn5ai_r6mH#5JCF%Mhs^1sG57U%9tg>7MAkV@eEpP4}TBLYYu} z1**89%k)h2nMsHw&#tnonu)3+mNPZTZ5PJaD%j7K*5p1be!BIqmkZ=lQ$UZ%WUtUJ z9GC08T~3|?a;di#kSCc(1rRjV)xur#2X92G2^hh1xyH#rS2dmI8Q_^G3YMi0jyzdtP_HfvcZ?tEqv7m z6LulA&w6cg;JYjjC(*Y_oXZ$B&Xj`y{0qk_4jqz7i8MlKk_XqWEp^$4Ik@Jd-7cvx zZS5vaJs$>%jus|&oSiui@F3Qju5g%4kX27;`s|=o9$qU|wlY~#)$lnR4Ji0{IUGLA zqPqgR0oij;3&`1j3tMx@LVcL1&_$R!W`Z#NUztiG9=W}km!65}c9p1DP zM8XgzrlnUm`LRd9T}?MFtNa70?Vs0H446M{9SLxHsySTnf2sG2ze zGq~Cd3f1vJfmj(CSdV-vE zbWQVfaYrNnSl<=LAl};k{eO=-(N2vT%*d1FH?_yUOF*oy=sS^9SPZf; zLXUrZp17z_iY7KN#SV$8#?#Ta5>slEwOV z`OJtgRb?5Sd|A4pVQM)x`WowZoR+S(`-FgH?1&4k*<5k((UCBMZh2`tC)^sktALwV zuS9sXNLVMAO3!?x$Z^UA^k@;dlX$72tbpak3*T}DW<`13@2*kRhOYQm_?@b>N435D zfxc+%a?m%YIqPVMsjq9(2g_(RfT$wIj>bnA<5ANA4Xdyim+e-~)tZndLQ8QRgm!2$ zFZOl=!xfOts)kD`9GER;}I>wL0mC3;gzmu-9H>dt1 zlmKiWA(80XcmQk!FR#5$Rp)Pke`l6ayQ{{2~I#dZ_O2SrMRncl6 z??CH!{E}L$0p7*nr)Scrj~dag%(^nrGTEP!>^m`E_==Vwrb37s)G+f7 z$eVPPerS!}w{42w%X2fR&5br5QrC7gMEWwxH8fihEYwh}C|exMOo!|JquRkmh-?|F zjuRi5`{-{o+^T3kR5tLyC^XZMj32Y!^ELE9s$hmBtJz#d%f04i8sZ}Q9i%4ZcDS%B z-Ka}iNERApQr%1@=3BlzHys@`waa75VSScf_hyUS14IF*|cLR_KP_dQQ52>@m#=({pEuQaV-ybDgjXiZkDu za!u8}rY&R|X@~;4#yUmG!t0bp_`{oow!ytiV{>5LE70e`t zlyj$!Z=Wd0knte+_r#R+R$s*&a9miEIL=@bl;gi*K42w~O}$-fs^eXnktg3e2YaF0 zK?fxn0B6Ise2O1nH5&q_&=Bc_1ITcq>TK1MpMk}ct+@Eu4az@$dA0|qK5ka!UcHfG zj>CUG9qJxyQd_o=`$N#^Z_yAN(lfj6oR0g5D6AB#TN=$DNsH_;*(==3{~S1>jBT&aj0<*;aUIBQqa$q zW;(!LlPtuc%e+Wb1$X~l=Q=L3zfalf5hn~0LLa&GoJ90Nku5njPuWqrl za44h!?nl-Te^Cl}y0R4_ALbA&P=Xltyy+A^Sv_VbV~b6AAnq9&dB2?OXss^;@pvPQP^gbWO&rI*r& z7%;y$TI*Z5;ou%1HkYE^wnC&qWfVHZzA7x`+75#My~Fc+l8a>eO3%a`PQbb>5e6MK zPn;eUFxLs~lFZOX{T#YpLf_c1PoTh$5YUYeh}Q4UYv&a?KGi@FhJ!&WQ2?X^3MMtV zT~B$gG6x+uDtw`mbl;)pO>xs{hY3a`C<`6^w-Z<_g4y<)XP zuA<}GuvlKo|9ml(XuCO4spfGjoskAPjL7VWDK_11?$S{viL8|$GjpkSYo-+OJ)geV zdp2HCCJefc3vY*LT+4@=cv#B6=tkv#VG_eq-?HGH*KBYlzdyXHVUszRztrGbqLOib zi8**`St$!kVb!DQ>K0MGLd+}d$h}>?YS)?Fk(D8^eLaKqQ1VfHf5Op`kM`M4Z##gI zg&&I8iHh+Z;&sj5N;Vu)?gT3ZBaqk@c9R?)eVuCM$V3|D2^&$adHZw-g}$ zjRS+eJO?*dc{c;gZrribmaV4QK^=Kl$8@Afk1muJ*@(>^gBJYO{ZcYinq$%PlILm~ zk?@LD?(F$0xppZ&gwusV*nK|Bu|^@K%rPt3@qrGLOWOJNb-CD~#}+cRGVIx#_Fqs2=m(5~|+NwW0eBgH-ADJ0VFfL``%)avtyd znj#Ul6M{!+e{2P{>3?OD$pSMjTr{V`0@Kte7~_1NTCJrlEz zw5Nbw3kex-n6CWy+LS<q#P$LSa9?-%i zTD^L!M)e!E`91_4Y%DXIa=XJo*%6NoRP1&K_lFog~NMesnbCsBYDy=x$xF z^Eu7dXJB_5H)UKb&u_8j75Mvvv)L=)8;P?DpO;gx#P ztE-VOS7nX0nPCpqo^Yhg3Q+YsA0WnDUY6NZ&AUWbisq^4I%%${;|#sa=s4c?L8a)J z{0*lngh;@9@%mb!==V+x?vsQ?_QUyolw9$U0>n zV_;n|qTu#3=&jVMVRBJWX0Zzqo8HnR(;*<0MBo+9pmeh|u_;tZ3&<%}YM?NCE=84J z?nb4>#)knVxN+mBP&FtsLLG4bR=e!`RkwjmXN5J%=wG**H2y962 zkeB`GP! zYW}LlP*K#Rw-nWCd37xz<#K0Y$a=VdZWoRfK@?#+i8)4wR!7eb#_5(lgsLl1{8&otkpGg{&&xypbyiHQzAeITn}cZ8 zy-4`vbr+}O z*gA$~bj+=LIedjU-EdwqFJ0^5iDJ{m8YQS|0PtK3{kG)DE--Lq9c2vaN@RL?zd%@Z0d=k`w_etEHdH<0y?iRt=BzdN z<#}Q6hiWV~v(3~}`*ip*N(OSR*pmcL$rgVDam~py!cAODd4_J;69%bpk8Pic=aBn+ zfZEY){+rX%M(%{I-j4$g5131S4A;R#)deJI35)xidOi zrMkFm1i!yEXP9{(HQY}OFj0l_z7GU4E>I+kK)`nZ76hhZ3Um?2hXjsyv3(HMAn3B1 z#Y)T+JM6h{f09A+Phpn|^W}vsm>$665@DSdFlX2$$(U@ldH&|w-wia@rwrCiYU9df zA~Se-w3h2+@XPx#W+DT(^WwDq@Gl8m@28-tg$6jAq%~{w`xjWlv$=$8k1us96I7IPMtv)^_IjqMw8j)%mVAt3A^+a+cuwrD9|Hve z^5+BCnz0+=tG;=z=IWsg3OCKot2-opK?-BC%b+G?Q_C@z$EpHERj1vjHo7=nilX0v z9ziq5-w|pbDME-qq_)4duuvh0Nt>D1(OAf?0j$jFL-dmA>)MG7E=Tfb19GWvaJlYsFjM)m6ty9wn}Ip2i6IPPCKlQdzyJfzj{ck zWzlB$s&EZ;`b9O35*O#811*m@0UgYAKfPqJD|2&W&EEX6)SU}Etr-Db-l89YBrNRR z;eM8f#JAI~#y%?U94@R6Tq6VIk1myC4+mt?_j`}%WjQW%OXfp{wF1&m>()8M%#9w_ z$m97IC9XM(WBm@%%+lowv6P!juVyBHW<*!iL@6MY*+|>UtqiQ+ZOzbitA#yt((cJq zQl@z=q1@v;M^N%hPqy4X`WcyQMV+!s`VAIq4)5b;&&APVqfNa7UWFC-E?6i>>4x2I zvUq=_fVb6Bc5q0evYvLM&_SO}rlYttKPfb6u{zT=yF4NT$7;jM#LLQa16p@Gy(DVt zwNHrP2~sRB4Yf|NNmG_VgE^TB6L(AFQ!;p-$zQva>~><-a7D7Y2BXqH7S_4^ci3>N zVfUJ$b@|v*WkI9W&{A{g0dA=bDgU)e`trjSq1|@*85RDjeb}ytE4?9q^{y%QPtGD&cm0s1_Jag>`Ag4GmAG{4ZKUXy$5_GKebB-DV4)1T=q zI~fuKkK|o{nQDz^7O}2__@KMKz6AwmS_6AZulG#9w2Jz>>#^Cj5{=mKwc(0_?qfeo zUtZ-JFL0e{7(~DCz0ToTJE1IkpcHS?D!=3K&wkH6hAn^f za;;7!){q!573LCwWt}Iwxf9>jFBq;$wgfOF^K)moy%BJr=%(wZ^?lVHW@>Nhe46Fj zpdG^o{eex9={3Wl*jtmzI$BBwFOe00Q{V#Hle_2Hd-*UwYR9OE-Ua3|=~D>k)Wa?R zPK(9Eox*qmZuxpE$4sc4S()NZ*T6`DyVp(kyuRI}yj<+!>~UU^tq$upKy6cgZ`rPU z;|JTQor}wNtF^(f1G>hg3L4B;hikeu?)6#ApSQBVHj_%Q25LG)eLiA(=HtP|g!(L3nGg%Y^&aoFp<_ec zfd#s4mU;~nHuu6_1QccT`^SAtj~alQ8R__@oC>*s0W`xw5c30_w={_|mqks?JFZDFj z&~YuKi^D87^J3+q4FBpx_Yp;w!48GHkOIc;#V4s4N=1?|+ucwnjD{jsg8)W9P4_5P zG1m}}OFKMIOrCcA&@bF7MFpS5M5jcwNwE?(r+86&=nbP_T2!HH@?&$|!E8)Vi>MyK zV__;e{|B?WE*8QuwHOY`k&UUr+KazFt|9k(IBId|OxIl*-0gz*gP>ZHS}p<3dt zyy~e`wiUOC%#KpxZDuQ0LibOOle7eaFUR4TKjM{-o1iQ@OD{!%qTN8o20o+0FVd3B z+)o}6Lxszkyw1A0T-)g=0S)up6TYBxRnp0ld&!OM1eP^1r}3bD|UypEaUxOX4{-lyS@A(F?KDWm-9~QNucttjxwYptgIN%n3|FKc^k~+r@>7&2;;>e^ldeQs zz5&Ie{>DO+M$gsFt3zWy9NQ^z^-R}EXzLd`no+GnrYeiY)sPtdvkaA=I1*x1 zj-7Kk_Etb}pWtpjmt*G3tK>n;s~WoQeu|mI#bkOZ$0K%M;ObISYK;V3^J(#@;w@-) za~~RtHwd;b^^_VRd-*cpX&(22Jqs07uMie;m&EVr@NPFEL@=$on;gg?=bc}cs65z`wo1go)Y^8Q(KGElMNU)f`nM33@1320;rUm-8lCJ56GA3DiK_Cg+QUZTgh$Y? zRRuBY%xW{u47>JS1D~Bvbe<~Wh_d}IqX(Hx!XgZeE= z^tu+2*m}Bry%%}2c$+Dgvn9B6F8K9SKqMK~y$D=OO)nL2b~RBA3d}QWdq8}0EUCq8 z1zvu)tg%2lCxsUY1L5vH5IBBi?y|~_{y9UMZZXg;YnB8vgLWH(zIuYbX zeXc;uUD^;816165RW{shsomc}e{i9l#H%OCr!J34O9rk{supBi-vLBQdG?(rCqoEQLI3%AkbmC@+1VV#-0 zZx+ENJmFGO>qAU^M@ZG?jNI8SM98%PJt?ao$K7Jl`YHF=Tj%k&vKXM9Z23+ajAf*n zth(e8;dJ-2Ym4Jui~)3+@zwqEvs9@sb=|&gPzYAlEXCJERS4;6-tlgg$KheznFkwp zyBAY>InP8Mh^wtMyKgi-?OpB}m84_o&Oa}E>%kv+*JR|yZ0Xd+g74>Eqoxd~zXv5? zhwkpa{G2jHP_eBr)0unwXoA1?fmhqeE2fI~Aq2<(?7;t-=$AXSrc5Z@B>o{nqYS-A z#Nfsl1id1>weaVi-&Os(KpcimIirjN|4P>$^N6D7PhTd(TTb#-jr6eWhRC zeJ9)S19Ybm0oddP?A;p$?rQ}D9zy|#3VH-NT+F}kk>h^o8?*bv9D82653Ea__(hZ1 zAJbJFlKdrP%89oB1{U3wW)6vN&p|EV8(ypQ+(%r|EYVEOGa#T`MDF5D{Dh^Q>z@yc zR7qU+2vkzce73pgc>0$&)mEAj7)aw;O+!Q;4YA4?LR-p6zN`ZsBTh)tcL@~P4ZYVEfGif^XIV6=LTz@)V zbd-D7IVSIKhmCJjfAe2;0i@pra8KB@`s)UMeJoKZ^&4({hN{cc)&<_pKgCWtW0TV( z#1V~KPcqa>_-qwdT&Yqytph09hnp8T3|cDg-&~*CJi3Z~j0$&|6NP?MF;xD>=`Ni{ z&GBn1|2K~caqU-cq&iC-zQ-(S{k`H*<#~Ir4=9;+U)}Y=36#&bzcw3cSsKzs(ASH* zId3*BC__PZ#G`YmKlVDc2dH&Rgmqh%3Ei^?{qJ5>z=u4Su9oCZW?l7rZYv7urL*;q zHdaQxMM?)K>+*dOdVYsh%hQ2gX>EoY-ObDD$BuoJ3|A`we}Z6ETcVK;hh5V`ph*Z( z{LyUbM1&eHQSLx2sD%VcQ66_{n4bc%*0ibgW2<$8K(eVUCw7-hq&F7ZOd!eyj}{}6 zytRyY_OnLkzK$h-pZ<{w%(jT&7GC4w`?O*z5?%|g`#1dJ^A7cNZC2rQ-Bv7)1NVfp`y5N(D-)s!#!X9ml@RfX9P3#FIX>*ofj_d{@!R98Kxzy+EJt_%$zZ>*r^C#^XtOT-}MCZPob9Zr=tKoAVCxaRvT0UH@8hV)~ zo}y`p`BfWhr9>NnQA$=dUpY#NJf%f=2>AzDAKl5-K$ z$P9sQ*4`N%LmYk~!&$q-qM)}ztbfMe{2orwK9{vq661Wbr{E#ny&J~7$u{CfwixNR{k@F ziET`76En-jv-%XQ-1>8ML$zL{ZqmK;1>qb1zM1?Sc|3h#T*o5SHlq4-_13!IE#&-# z2(AviP0ipz`_Z3W6@c{PlkOS{2_ujSgPk`v_6|e|mZ?06t)JhV&e?#GcR+`ozhe|* z;^}l3qAZW}^Suf2Rv#{WR*##LiL)DzKsoi!mM!%JvB|G4eyN>*Z!%1+AL|mSXOQS0 z;2U&5_M_Hpl)(Ga13w!0s5uN#D~6|9`${$RvWys4Cd=NONo&l%XVEfRcu(sm2d!G7 zE_<4V+oi zsr$4P4iyv_R@9v6&sdf04jpctsx4C~SzjKh#ccJhUNKa3MKX#g4@JU{q?gXLEPk4@ zYBd3lbpy3}1}fxeM#jN<10k$L4AP68Ctn z54N5(73GTr@HGG-sFIuV5XDej6Z|8oP4xiVsZ*^oi^x*w%0?;Y`pU9(C0BKZ=~H_NdwZt4dYk;7B}F4#qDU(2yFLip*I=2 zd@uccK~|L&Qvh#r7H>wGYN8bA*)88$r03V@%0qlU@{u0tIb4kl+=i$F%qNSSiz^2K z8z01934k*i3|FKYdKV5KaKMqvdeLy%qc6}_uOU~vv`jAxvLq-;Y&D6HI16V6h&St& zr9`AZ)Y4%P>*`Kum^KJr_=NAb(CiC;!wA(Aah(^1QKiE1^?7!~dXvmw!iU3F_>=Gy zExCAjlRE{fpa5(NSYs|mWKH2pCeX>Jst8PmX$|)BSTDXT9?U%#a&fKBJA>PBFrJ-p z`!KbQ*sUE^+?A^nt2|tz5ixV-bJs|~^AK(q^ANaB-$jg8Wo~r#zu(4S7jqYo@CnfO z;zSx{>*31q(+Yo4(uh)i!i)N@jH}uH`cYij^I6sF@08|@vaKK%isx$=+`T?H?US496-S#P_ zNCkT9mnMROL@I_y>TvFG8}H~s-lmo&CFdW9s=f>4ed~%mmDhVY zp6&vrUH69oNzpIB8>mfsDm$s}dhIT=95=B`UU#2r-4ak@5p~DO} z)xt9R+lOm+m-mF45HA~$8=w;Om4|TmYtve`Hs%AsOkSYX@PJZX z78}kjI`)aiAdib<{uhouH?e_&g(~X`cBvXzj0#1R$Kbk?9%0ae=oTM38lujYn7`_~ z6SV2?G;>HGrLw7TsMjPXs;1})yOfWujU30yX^pIPi&uVLMfEqMbYrE(YnC@eYidXG zK21diG;_BXpfe3ZSrvzn_E(DdTT@i>E2EnW?K&7-lrE zg+93m`_WMdQcddyt2KAHYhb2QWnfriq+Wmd zyRn(9`&>aG%i09x8P`RNSA-Vh{&a^8V~ZujnnY=?n-;etoXG&(0OX*BaFbzPQ=-|j zue^`8jr%v*wg~!}>g4jgJKpaWV%L9g*bFVWrYv^JH_DxACQU#K`qz z$5-`%<~$K^ub~)dhX^4mmiGz<<&qbjG>h6oRUEt8!p05Eqb;nI{2B#^hz({z^?sv) zGxyMZB*T`bkagG z?~26)?@jdbV{c$o=>xax1iTUgnreO_KG~vN38RiC9ud*)FbkI?r9rpk0;ym_69Hm= z=p6nDAZUP|Z7sg4xYf6N(l*?6BPtamFaoo!c|gw1QJh!Mq^$L{Nm;D%a3h1fTKeEQ z;q=owKM5`6Sjiw=OLWJgsjpKo{ZPv_e%`yFbS(7GjCj z$8}qhJr$SV(F2<@jjAV;5to z9))@juzX030Kl=uU}_K`hAPy{qG5`?D5;Zw*S0%1`%ZPUHCFC&H6LtovJpT?xym1%cDYD)BIfdGNX_%zgEs`eX zQ*gEMU4&smQ|jr^jGS^}@ZjUM$Pc%D03ETRLXN{Dd^pydh^z=n>gDk)3H6^1;70~3 zG)#D}y-$}iAjVcMUL#7@9}%v!aJHr(p`7ZIy#Q^bcl|4ZpMiKAENcp{zmSXfrQa46 zyt+0_RJj9S5&++yO)*EUIL2?5hsBcB)C9<&J4$64KSzDzwscs^#hxQdWi*nhLj8>kWR_AMzST!LcHS_h%i3VIr^s^gHuNEU8@z&Gr6_V^N~$~R8@^7^NyVSx zmQUVhWLKm7kSJSZ$%@nQp;4>V3Buh{-Hc)vhPHjWiGz7x9`M_pQ1uR_1aWIft6HIS z`n|tNbia1y#wn3xijs$}rC}M;n>Eo1Xi?5;i)O-pSB4oZW&EUC0B43gd@vzk{Lydc z@qtFKQ^WZI802&lCZE|!vuH@%*~VBhs3|AO)in_kWJ7JM3M+Q-3^(pV{m?wnDt^Sy zXNliUCsaf|rzrI6B33&vPsFn;IFMJ6h7P8GhOSr)0%$|4#6aJoJwJI!P@{Ex^y%vF zAVjkAw@?#!Hb8r%x$F6f2g2kals51!el;@Mj%utS-VvtK8%L^Y;G3&epx$3`hi#P7VKwBqi zwBwOAC>PPeytNH(=e~=F^#i`{{w89$rKh8IOv$69Jy;B*KdR|2#@k)I$^n zQlSxQochlj9vCvZNhz(h>2|YejW%MI!#ns@I%j4WK-?an3e4HZUfMlIjV3ucBmm)=>LUsY=>~sdFN}lyyl*dV86$Ql#tS$8U$P<*(u;K zUilym`Bltuh%vU1s8`X*jJG@zDC0du`uXsZ5O_mmuCa4?DR8Esw%NH{FSte1W+Wl8 z0e!oL{JjBvF>~-Iz~O-`HuKjpZi6SNEE$D6->D$1ek6W(WG)suMEPx8hFFbgb(E|R_z+O5J(2d2M zE^w@cvc`KP9!0D&FnWrSjo7xz+rW5ew7kUtNDP2y_G5IDYZP9PnCSyj(35D3WG?6b zw`Ke$n;0^(NSe^?(xoaJ#?!bu3$wzFYXjpuPkUIZ zs9uvOUBFm3Ro6pN23SU@F)B8Ep;-gGdI-FonJ*nLO67A9e(P7cfa8_MhuKFu%R3}3 zn2z~+p$@SeEZGtN1&#_9vm4a>2&=xuJ~)dvMRk@oZ3qoI##k74lzy*&WAr6FOV$rM z+>^LhW=$Scw zk55yKk+fjk77|$1{F}tLG|IzQ=k9R;Y%y|s&>|R*x)DF&cHe?G=1f{fULhMo)kfD% zsgmthFX<Nq)q(*?9wg3E~QpdiJeOgFh=c; zM=-Tv@5i7AG>0Jh)NYze(uWOMnIAz}8IDG%y=7Xp5OpVFZruyQG3#7MYSu+wf;{rl$7rH_qAF%yN_`I2fh zjcIVu>*?Yhv*`F6g-+pW z-64;QHdPKRVOHA}hNOiRDaeH=7>MsiCsx@4CvOb!-&sI-< z-y|9b{#Ca2;H0-a|16VDV0yYw7S3-^m)Yo<~hN1iteVc(e^`HBcp+DwMNbh zmL{vxKyPmc5Mx@Bc5R0F>4WIdsXxAGK*m&VF<}=eGu+@)FvA=#LnxCm99}M_87lba zhTLKRTXy8TVsgEkFbU%gqIRP>=-)`LH@^8MwqAZfAS#Q8LBxUrlJ8Fqa@-gANU0x93p91O<$o$hUF2tKNP=OOy%B-` zSYt$;!M5tC^n-2b!)->>j?3#iIXL+gAnEh#5eq6312AHh3aY{shcQ}Ve%yOr4MW6r7jC;whYd)P8;vf2%$ZoA#(xAN`lwfo zLR{OF@393RUdKAwilg4FF|LA9K=y^#ez>!cuup#u-DbZU;T5u7iJj-NWKd7}o$$?x z0I0!Qh14jQolul2Y2T4=tbsu2pe#fIzal2c_j~tjQG>XkzxjK>1-|T0-b8AtqN(FM zE%BW&27G3MRZ~5-0SbShfD)+|E%}SQ2*HPt7Nn3VFmd4tj=jmT5Vh^ZXaL4|Asgmd>qZoNNG}zGEv8Kjaa+j;<%u7~mUp1% zdEf=UqtrzC&+<5gg?0#+q5%zSe;~zgHD(bt22YTrpV`)lWU+`=lx9Y&*y#{DnSPPK z?|#l@MAuZDQzR}|UHZy*fv$V>hogiA%H&)bGR=8qG?o^IK>D?*k z-&hqIAjcSNRC1=idwN2|6+PQSe-OZkq{3N2zguTVeK#33@U>+YQp{RDzd^)jD81n# z86>6Gi#D9*(W1p#S3iD8Pac)Ww+(*z?E?vj|VmG4`HJ_LB1|<;E2bk{h~$l&PES~cko6aB!B>V{>dajKwuXjl^_NJ2(cjvMGLY4U{~e-c$I-m zRH#O79#9BL__2Q|guBWR?EnNfZos`0@Tbzn0>Y8<7diC%axv(tJwn>}(6|qM`()c? zi3Tj3Q;BXT9Wn|e?b{-`eLV!&USKnmv${NT7|`7?)W~vyY{HM~1=Sb#@=*!BbwzhZ zmcDE@1|+R`=f6Wf0cy;J8cU6G?EDX?%9A9bkP9B&o{YW*(ft-vc-Q5v>ST!MY5OL_Rd!4(0T!K zi*@lWltjLo0YeYOjBy7|FXQA=i)7n84*P?!2%-xzR#kN8B&(FQ86j~!!B1A&(VVLDDGNbBx59Ctl>vw36{3^c(cVtyw*G+? zfsOC}eg=2@bD>faWp%biL(QYbaSm?@oowgA@#5qGIxP{7p?BBJ9Wswp-rg1*4prP1 z90q)t1R0v776B*{weh&DuPnpsOd{s{ohS3uk-5{a0m0Be5#~sDw&gH#$akzq$rQG; zgUc%MAo20ATq+E{Fh-k9bG_#w9YIhWJpl%ddSe(afb*u5sY;u)U>tIx5vAB?PftF_ zBgr2OE#>{Z;f4%5sibTNF;B&6GKxR8f8{sjV~}{^1I>YVxQBz&IY9~s6&Tw94dt6by`}gq>5!MmK~$TDPwC z2GK1o@#C$zg26uHmA?NGab06=f+TwKLftQ(o{%XBJv>Q3q9TM-%sqDe?R+ChE3bE# z6SdmtYW*06Qd&xE-{shzp4n-y>h$Y+dNA(ustJ#@EaoJEs0_{dV~A0sh)nt+vSVJr zZ)|yG2_=X3b0D`xi423R00js>j1o+&5jX)O6bk19We-Ns!-oUO_?g1HD>D6$KnZEE z$yl?xbUuNXQl#e*Sq%*ZJ;Jv##~pM!^ol>W!z@F)CWL-xV}QQX3K|MVu9-iL6fh)s zJPdqgcG{mwrNL!%am(Srvp4L=1K9?wHW#hzBQ94xJa@;O2J83~zcbhJ?H}^d0&FDl zIQKEy^C!N?br(h2>!YpOSn<_orOA3XG8WL|^st za%;A1F3L4*B8nI}amOt84*jO&6CKeDbm6kDaC1Tkx5 zG7W?pJa<YYYoXAg!2VVFo*w+te;;%9~thTs0430lK$)Y_ z(`%+PXw;jmStmR~K6%a{6S3pG=Mm2SE6i%0aH6UQ)n}5N0ZG>!&()P%j795fw{Y1H zQkvy79eZ2%gBCZM(ER2UTvzy$0(yUl?As>FdV87 z$=rV)RHrzZxV}>wi&ZS!;*(}Y zt%~ENiSBst3h(}TU9)k_k}7NW1>Yvnsahg!l`JQGQWwi28x+Q|MQ_28YohCWg;QdL!r4@^{j zvsHyfo}D5&rX}dg5Kpc?H+NYjzIbNta&-H=Vi_`mkn}^4EnS1D1l#&nNcMz0Jmqk4 z;~HX25G10ZRQc$Y3BWT!n%=aIQu;HeR~$vd9=s8;d_a)fG7(+;W3 zPII%$X(ueucbNzF<5wB*-UZMbxb4`DAf>eWAj?9hRdh+k-^VV~AaP>^OprZQ6#jp0 z!*i>k12|Lq`=~#+pMAx>0bt6Kf&ul|(>IV>z6~URY;y~<4Ll7^yu$%7j25(BpDFC@ z{l7+C+)r$!j+yp22a~a{mYv;sqSZ)is;^Lbs6E>n&>C(af?Ijgv zeG7VAY%VTE+5A&2!wLXZWet+*%gYt654Y`YEQ8vc5ft2R$aBr$-91N02Lm&f!m zw1|9Y;Szaj%cWL1J$d?Hr?(hR?}z_5y*guu{v^sbU=65{>ju5tIZ+TV`*llT@N5Sv z!L2ATF~stQ@}Bpx2NT;4lTcmTN5U7;7cHGIx7e9p9rG#+|FAdVy4TY==$TZ<;VmHTgz7rg6yQ>ApDX}r0b6s#K7HsvtZ@; z(9(Y&FLQx9b~Z#z5)C%H`tR-sDIO+hs4X9dcET@1;K{y_6l;gl04l^H*N}?b(T6ae z#nc~uV7?*-vEO1DFlfcSwjnBpDD=5>15c zdXm8(c#MuyF#ejOlp$&%4_`9zT{Z`u>}0_tLCv9~E{zBTjWkEnAzZtA zx=2qF=^RsVOElabT_Dxp?^tZvc;em3k=LBcwGH_TH}Bk&cDGT%%y|XZ{eOJDcT^MD z+C4lDDk>lRSTKVI zdBSZgPY?75EaYU|LC6peE8eHDS<<#Wy>JN0c(?sEe|9i4Ja$Jd`|K^7vMAG7Rfqmce#OAB*>se*i4~PPNvo3El3-JP zFA*ckuD&b%1w4|JIY6`G^A_5=zPrF=NdUATE>)oEwG0Z#jUDm(CB-n z;>|&`fz?T_s+Ccub;X41f1bNVm({IW5Z&{~-^$3TR+V%YSX2~?S8G=NILkc%ECPLE zbax7KueacyK2IJ<7;-g>8%W?!SZ|BYy$$n|$ImQvzW~UNqrOOny2ord?}l7UX#f=i z=FwpWB_pEG$%K8^F>t%x=pc3y)T`!YHZca3pKcJG=#_yToOad74G#}&DF2T`3JeFv zS(_z4EQXro73Rl_c)<%?96p^<4E~7zT1-d-eteA$?Ui@YmcmlSWvUb6`ANAF1oXI0 zXTNpc94>S=jqH~8+M3s>C_tq)~Cs&zd-V zT17cqMSUuj`v-%bXX-P{Sm_l$t3jN0))BWcQO{jA`s`paL

>zY3t@q?4nl|RTK80uR)0rqZea#TPi@mfjRXwbx8jRx0 z)#2iz#_{fLp}Yw-jCpCfk;Nw0d^Lp)W|i6 zWj4-;iqquV2RE!j`kVs}!BX-Jug$0mU%dP0SLX#~Y9%2@W! z`n^3>9K4HTcQ;LbP4bb#QkbwfxVKm=F{HZ_PmwV&WlU%4t*YqF9_)C9AeLd2B(D?N ziqc4I>*V54F}a_W_&VM`>4lxefhS5 zdDYX!`=U>IaN?W!hcy~wUz?Un&i06AKEHQ~#yMCInzG4=2V4F*3Z-01#+{lZK zyN;0j0^`(t+F6WwbNi^UPNd72d$)xCKwH#5@jg-0A)@&?>weeinM|POm2tw(*8J?$ zxa%9y&7NhG&1d(q1WWA|;%-06P6|G)rG@HBgL}dzh89*39WEY4e2p!PQa(It(`Hqp zJwu$>XbSFa{LcPTkb!$#c^pJNaz;UjS|&^bJo%niH}NuOPxFw5Qvam+ynJz#><%n& z{7I#A6O}(GdKT_iel^6zou)YqH*Cbrw(86s7$Ce>AkFZnNe9R{B@=cn-HG?ARZx>8 z{*+#`PB;$6c~C2G&EU#_4Rz#yMoPWmV%=ncZW%gV-!?s7;DH1E4UCH}WPYLY|wF z=1nhaWARj1DoFuHjS0203CB{=Ww#ppA@WkYD zNkqvwO}nz>^=dKBl8KZ8*Nmu(Lo1lvZc-_8>qi``R8F=6R`iiTYHCRSg<$+cT`35G!7k|-u zq%uwn@$?gDZAN=QeJWaStHc$icsa>E)y9AAx=eF)W6S5Yj4)m@j_&*GkD?9s363X^wX8;#qK6Y^ab+S{r&Ct<|n(d};}4;Ko404?tT_(v=N-{Os_N#`*ey*6$9y z5nyliK)V_#LH4ENwB#=cjxB8p=;(nWu_$HWWYVXIjHt0_!~(eU-aCs zjsLp*&XQV#u}iLQmSbH41cGL;9PH|s960?4u=t090PWZZCZ*^d*B6dP-7)JNXB0@7 z-QOANRk6eI9bc&bD^MeveKMOAHYjG{asM7=bMc3=z8J&W*w<+rhP>?I!a%luurlT8 z>WL_?3W(%S%%u$4F!>NBFdC&+%38OskdhjMu&uV?B{x4<;08mksj3G)D^empoF+;vc&F2)xk)Kn_1BOWuB> z0jf1DmL}n@T`-K5QR!A+i%pKEfd8J;wL9&G7|d*S^`3Ael#$Y=E$(Y1)O4w++r)2| zUZ$`*(Zw_W4?yr=hKbS=xBlNw5!}=LmmO4(UUYzW{7*M1&Hkoo=>MP;*iE2WD&@1w z6~Bss|LaSCZ1yQrfKls>BZg!juEzIA&PcMB-#OT>NdXhUD>bZr+2rBo_zdByTe(_Q zqLnM2D^HsKq6F1~OhC#{C5Y?XQJ-?@blVCcFFnEAggx{uw?}PZqSZ3;Zhoz*iq>epC28 z7;x=Hxsu=Pr}@40-Cit>cKEx##0~x=91-ry$;rI4#{lljCMZ2=@9{XsF<5{e41GA%O{Nk-J{(b>${xx`YVPvxDY~ z`ur~^pF z0C@FFulkg36x@f}b2hFU!3_GK!AzGbxcm#?{GjiMAJ71F4v@*rrTyyoj{v{EZZdpP zL_hd*<@>T*J_C&VAL!vhKr=}-T<8Iu+_d=rmB%RjM|ciBs{|0A{DwuQA1pSrxLW~U zX5#DHVURF7mrz7vEhc;Ny(QdHB=x|5LYn^v{Wl#j)fCcB@>KcLPyN~Se=;WjqK?xv;L7c!4 ze(nZgzumG*#uhSP>h(#HO!5E4MV+T$wqH+7_Q+6o66Su{P+TCz@{kfhOL1Ld6b!Y0 zHYy1k{hdPl1_Z492Z%;-b^H^octwC7z`+6Bh_}!CRR93|=gR%{RezwT&k7j>|H&b2 zRsQyT1d!aHv$yc}e?`1XzfsGqK=YErIEwlQv$xL-b&MRRGVEwv1evRj3B zIJ52mvuF_Hjy+PJOaSrTQ)&_*ZM{+bW4*oJePSb-gv*0~pCgzHWi`R1EVe{)|K;_T zq&8SNf~3KCf#~3mQLG{m|E4iOJ7j14#ia&b;*b8an>>eg_u*G+uX6yyZAd70I~5l$JYg%d(-u>sLtwu3k8w?0zJ`w7Lnl2h z@6Rd=62DL{Kz;*R7*E#rpI~Z$)>~IBMYENH;*^X;7rtf(N&;uyBk2Nu*VSV^B8Ad& zC|Cc@u*_U%CF5Xzchf-8A>g!s8H^k$WJ`%i;htfVB!3ZBXx|vvQTz?t9X&$m-};Nh z-yI1tHePGnl(Fdm&xWY^Ai>{8b(ijFJ{`u<0#ALQ<9WdGG=)pX@Nj>-o3MAWqGs;l zNOwDsmPFnryYib*eKR3_aw^g??=-;BCt*|0}mg|D8eKN3RAF;d^MN(CJRpj=}BVnmlyUoSF*YCJ;uf%Za1$-)KC1J z#N;2T_?YZuA+QmfMxRe<2Xfty`Zsbm(E^)gU-nqzkNy6U zpi^JkX*^*=t@t8O52As)|0|3M99hTD<{1N`H<|X2Kef342>q03e5+e}rI*mo)0`CL zMj6w`Nh?YBf7y=LO#hEyRej}SO43}&^YR=8pkvv%{>Xzqtvq+7?V<4(h&l&QIdMaYc>T=U#uAcQ*uUtGo9Hs4l#U2M4MfaL^Ql}(}3oknUNGuq- zaWhaQx%kTBy{)>{ZB~(+9t)-v+U?M0@$=+KlT(O|UDF|M?i*J@MPQr-bnQ)$yf3(O zfm4%IxVp3iVIP$xd|?5G=HN!~&SGYLnzoQhyV_b0!yon2Yx5G&@PiF|3+0P#z3>lO`!Ih+dM zIT1xS1v5kpWRf>I)d3}hGod4DGB_dH7in@oJkc4GZ9AvJbGqB(Yfydc>6j8ppc;K( zDl$7`tlv4h=?VkS@1JY#`RTreKjbM;+6f>ey%#ENci5iPFSWCr6@U`Yo+v7H41Q?A z{4yA)E#C@MY}&CP;i+<2wwLiZe(|w{8aptw`{#;F8gd@9lLHY=I)$m%1MoA0`0ep_ zI^XRiUNMhS!3=3Tk1vhQ>|LjA#LNtE*s_t?=S(k2VO1PN2Dk8Tl|?|KkmgoUYGCkr zBL-v)#0JHD2)+-G6?Inolo8c){16;C1;Ia|>};DE z_dq6+Lbfq<|NCrEeWprsiqgfrO4c*!j=}lyNH(Hy8H-TJOO1z|sIZB&)xXUN==@+3%k`?_u2L}^Tk*^{>V}1#eg%F~idhWoG z$hRMVrsv5V#~ij9{9p3GvR9qz*Pa2-L6s614PoDMn+dbV>j4LF{tS|woRmzMG~_nn z*M$QPWj74}4Xt_oHBIuF*?~uW-Cbc(Rh%zs2q`{qNX!K0!d@3jNlBTR+rJ|iE}Z)n z!Qdqm0G1e>$&YAYx&UHCz>p6}z5HLoB3H7?>?4hoG3j(c?1;Ys6BdyL%2p-WOsAnyp> zKeO;o)D1*lOn`>wKjM|Xhgk_{37IxU2B+OZB+735RI_%yviEazU__v|dUs=5Yk7Zq zOY z#rbkyW2F>7oFjpsPEzI&TR7HC5Y2u2Tw*3SXR4TgN|=b}0~21)na@YUqE1Oxx883+6w4*xFrPXo4lQes zyq8eqzmF|k47Z(Km#eEJXQ2ohl(;k_wWC>@13nTPt#w-ypIds)T1oaBZxOET3%oNQ zjP?6LU7e}z_^vxyoX6v!_=G+X6#4L^REP+)HNk3xNa=$~4lPLzkI*WPkG#`BpL5hP zffTNzCJT&B(gEUEJw9pKEyp1S$z3mxhG!d=tuFi5jiB?}MUjS3=7DE1Jk%szln5xU zCxcA6&!lx}4lnfoOte|)2ZAAGKhd(K_F1%O%2FXY3U%*i9owX~6zsg0I9y$1K7+ij z%sH=JRB|9aNHyuh=ttBSr5hppBwZ{(;7elIIQisQJvT$&ExCaNb%w5cvx&?p>jSXF z1)8-(a;||-<$jz46`=iJ-s#s9FzILsZ3W&O&bM1*LNWK-nZX?1?=+ITRB__*u=zuc zG4$0$&*t*QvzcUFQ2MR-yBxtHa4#X1KBkqq91YZrDYdV5j1^Um@FAxa5x-KG@4fB6 zioqUU-8-Q6KIJ{!i=~!Bq!z7v^>JS}(yE*DFymy^q4zxs%W-R8X@ej3Xa zd58LzJ~|&8SlP8iJIve3qBtBf;aahj<4PtN6a<377kvtJukSc9zil6IUE<9MO+s-p z+07T(_LK$HnQF^p&gaxOFb6e`7LwV#9=g&C6M*Vzv;qQ=i!(3LD(lVskD+YPX>HOx;H#dt=^d#=_Z zKkL#*%Ac56pCY-tndEugt>|=UY=ZguLz!C!VS7S*3+Ni^8f(7yT}pS#4ZL>~u>Ra( z`$9OMXqz45*a|1@(oA*K!C5z6ET5W&@1dBp-OQJuedY0G0;}Cf5$_~HFs>|PE655q zb|45a*H;r6lzj>$giJIJ!f*CGkCL%9;Tb^#N2#4hubshPf{1Qy=;db+t(GJLoACuC zq3YRZ%_!O-8$RetjJuYrUsM!_C9-h=mLo^h#|8J>-^$Y%Ub~zNI<8Rh^_E;o@G}5| z1;#4@8T_qo$*)-@Pn)^g{@};zd<4`XXVAl@4sThHc*1hFU3;S(&@9_xZYvf~HoDXk zyrafL97nmS{hpax)sQ`T6m1>cqr#M*KS_0VGzD}KM{b#rBe%>g#T<~w36P4ekDmRy z3h@_pZMS76D>M+`j)8lTq>CenkLB(PCkF%_gp8Z|XPUhd9ADJz>+UL7bRgh01l{4% zC1L%oc=!nB7RiQnG4D2mCUMV7Z|8>=MN!xMhsD7jrIG5^T7@O|qX!dC8VaiBTr0Ji z=>Rdn*{U?}fyt(PQ%Ep;8i<3Qmw`NHh> zpF{GHXd~msB`|1RJeDt@rZl}{uSBpSe)hy;2ySec*-u}9702uvY0_(>T+K#=1pFSo zUyhF)q{g)ze}q`p&8lD}y}#va<(1kPY96-a*ROEe3lBWS`$IMyX?&6k_wYf{M%{Ja zI_ad6QKIfmkffPy(v=TfQ<>c6K25UmxXS;>kOaBZ&R1KIhNllEn?ViJ1ElTFS6h2} z$K?rG6IT&IQ1o-JwOrpxcMU{~o*1X$V`nLK-LSoN=Mi`gff)%HHLS3As^HGT*<3Xn z^L?ci>_K!uzn$6Ne@cR$--HZUyggBYG=m6ENLz7W2nUziVN+AMWcvN%HSayDYAF%);zT zf7Fbpn?nlAynSD)e}%n(G{cA2j@yB9b=2CkI};JJemqGPBJO$`y4Gf(nPns4QC1*N zvn-Dh_?BK!kbGHz_9|bk9W7~%o$R^Tx8OH;I!`P!G@+W4$#yWc0c7>NRzrKWC9IXYF34L zI*m5nTpZ%;6C0hLjjWf#-;&x{PE$*5VVJ!;31(AiHLVAJge26K`r(3>uINc6w~0T$ zc6!CLm*mK5i6;o2KrodS$790jD$w@XF3A(; z+`w|_-fNH-W>HVTljgm|PW_X4H{LTgo9fPbhFD_%6MPCciEnBGpk?zhBNUC260Uk8 z8z^$*)CW<$s=vVmH!s4%2-^C&H|dd7nUmwzi}FJ;8#|P@o1po4dWy)xu7=x9Y^HL2 zLY3>&=CoT)zkX*h##3*xwZH98(IqA2J}Dp9q#mB3Zgn|bkxMPPOz>PEXC9_45EpaN z((k=6$l6oo}iXQrO8=mu(<4KJg})4s(k9FvfzRzjd*QZqJbx`dfRr^XY=$May6fR&c1#- z+uWz&3-0*{i7y_V(Xgc*oS+=V(Ec@tq0Z853|1HnQGnYR7~V`RkQBy8ICP1t`^RZ^ zc7e2xmun^;Bom$qYW6AMI3JBZ++%+V+^#_9c;BYW6(wr^pc8xOr=7b_w zjq*~TC?+!!usMeC3o@h`#ow;T^ZJWPE-0{KzYh+Lo8Ou_S~7P3_VNdJ;l;U-1OzDp zS<=0&k{8UdjjYKtqmL3|DRUgO%UU$5_olzWCL-j(sxqIe8lS@+?qll(aD>AO@;B94mBwYwd(a(;OkVB|Mbrm$z z$vo~NquN?w5xyEurVjqjBzTy+s){`CF<1X;>(mr zlALj8td&)i@LO}*Ph_e8(pm$(#otL=moz3_o_cW92~_d<>n8<{6}`o_LKF+FYXhZy<5Pb)*bNUZvPjbb@_^0?O3{i zu#vOY{L-tNKG^*O>PfIsOtB)0=1M2kni0yR5QG2y{#%MumL@i%y^O-z&fM6`lAhlx zSChCjUO=h9+?iKx@#V{-=tHbtaqxNJ$HsdMkno>foa8KVB|z{rUB^CwhtJ8G=Qe}yxyG= zu0Eevy0jHQ#(|Z@oadcGbdyd!%5&Wac!IxvjltrU(c`){?9lpX2m$&sN--a36s2<94<%bADR*vj8US{e<3b^v&Y-*rB)k$}#R{ zt%*bb<2xt1!fxEZa{88!RN@Ve#J^rM)%IV#-@@~0c=#?|Fnr|F9s6U9!a8$#^o+L+ z%UaZ<-J=k1*tDXJpP&Zv%U=$-Bjb%*y$&~+mo&VV-BU&?+*g(-#z$5hx@H$Uh>{zq zIMKDo?_NNhk-h$qtD(x?1yd{*at_f|u}9b^-cMEEFnpM!TQRyZ zduGXq6s3#w9DnBw9Uu#Uq?$c0UT;o+tl4WZ)ShotxYMZXjvQ)cl6a}y^pKjTHM!Ej zD+jYIk#F>)zcGxPs(Dw3#o@>%n~O7~T4zJgq50e~@CaD8ApyJA_3-G^n%4 z)6bQ5E=S*rLoxO}H#MWs?W4fQXkz^`P!#7%fJNAW^M)2e_dP!KrqbqJ4>mo{*R?^L zubZ%TA|M9F<&sor?gSgzgn(5lT$AM*9i@YvMJq!i_Sb*D+niW!z|8uJrRXxwKUTmhl8$@DAPfoI4+c_>%+pBt;!1-FRNoE&IV%dOl7{>7DwM%9Nvc( zM4PFLU%m>}dUnIh;MBF#8~68QRT5Wr8&r;K!45Wi2!Clmx;?7-Iz&1i|A`fih8NGj znzte^5I${pXfT;K76CyeO`xt=J1^3OQ3PzbAz``Hqxv>a?~6l9K2N05>QzckwsO>u zn5O6==^z?`w1}n*DVcQlX~AmV|w@PWlfz{Yhq94K?FUxR#!LilY8Wsd{@#V6iz{e(_dQ! zq!m0V=41h%`pwFpN{Y8~8|DjJ^b4;&OasR6mMXq_ZErV>3A+u2hS}1XR_bzIDW?Oxa&;pVOqWVtto@pKg5KF{>Fra~= z3ETI@W?YANZd{h*3Q}_g2Sq6A9i%vByZUmp^%!lkUa_E#n6+^&1!j%M-KFJNzWP@l zi1p)@oGYgsKW~R?I}6Pu+chs&>3zo`8V1<2o?t^3xcT=h=kB-tCAwxhu?q}OIzdF5VC5;!Qy8hTVY z=*6?nN?MG{%tBI3Z?Qty{_|cRPNvIc*tD=@`Vh|6mXrfd)+#xW#^l!bbIlBZGdN4 z6|HlwNW9rXY`~OaC_Mv2#NJ?NTqIM4^+Kdq0IMAntI6aQ{bDk(QpY( z@{8G4RbYKlERj#VBWcj#j<(Du=-1E8QhB0p3F(WWlHmG!m7z`}_D}PcoNcT~VeN5X zxACd~3s%qBeL&zq9~BoBwl%4|n4&6h)vKO0oYI~sX(aqr;mPy_!8(P+AA|IuT@}fM znns5^?-t)$X2o5vRBo6$x#;xk$TnNX`Z|vArAcKO)o8MAO69eO>LbF<)UC45BXTEc zxy01G#XC1^`|%@cnx)R7lUuHnJK8fQsnjgzYKd>U6;~>}1P#pT*MDX&CR1)hT;v8f zl6d?=)iL$CQ5Ym$YW`TIn7bfV?LH=#79%4UFAN_+@$yxvJk(zC*7q#6lfG=p3cJ*$ zJiF1H?l^h%_$jq7+dh~XUp0T87QOv9?mCenuy4+=db$K8F?voLv_3^V}Yxy#rP5T!kNqm zCspn~xN8wPIqPMKT2>~*hTi@%IsIBA{i{PyfomN7TbsYUwpu>eVm=F+e|P7jD3*vL z?U&qCT8F^_SQ&llZF1kNFzx`sGOt?n{3s)?!}T zFjex8P}L zV#GCR@^V+}X)Zce>eSF+v+P47T3h#hPpzi8KoV;*6 z*FOW%t?ahPbv3!`O|6f!>4V*qVx1MAU-U$xV%AYLn+h{Q@Q*W7SeE3+7%FwvuvdEf z4eP5xa$@xi=8WfFoZAdlB*T<#^R!K933^S;EP`JCqybogG_8j@_842~_JI5(5sVGQ zJwDiB-IAl)=|{KQ)3bfKW{vW$6Cq^pJYNspsXbkZ;IV$o5kLKGxv73Ie8}5IXJkuZ zD+nQF&DFP5+7Fggg7L%^m4-s;5WyXpcL5s`e64|x^pa_vvuP1l3D3?xynY+06X|c? z&VZ6}^_ez^TjCxP(oBc1APV6Z5bKll>NF}=V@M2N=&>dVt~}nY#jwQrY;2VMIGC|*|739G3{qqHlz@h{$_fRHQZ2fYKKaSlx7217P0Zp* zGfS5|2bkpY{;JpX$h^>wE9>&Qj2)HGOY3Kn6ZaIU8^oT~g0nyOf+4k93oB zv6^Y(by*BtoOKbIT+_&MG1?eTm^BHrmF_1Tz&H3;waxyXPP+fFc`E5~Gsk`*H_N`P z)=e4(iEnpYZ0)ve{8c`CLQ9=Yw_?&Uecy*;UaWH>CX5`2&yj1^XvpauR@oV2 zxJGy;G-)z0c3EuTcFkiJ7s1KoRQI*fyfQ1i`S7ijPpn9aabw+cWFTZ!?%vp)OEFgN z?IlzYbBw#CjcdDh5&va2Xp-}UtClBXQ<0t}nMb3)=-m zpwe~O9LQa8{mG=NQ$H?VU@kd_aO1x@`9yfJExG7&LbCV{_r_9pUTgA)y6!Vy1#h`D zY-n5;6<8HB+Pi*YeKnv)yXyM*%-1Xz1Is6|B?j7>g}dnm4%-u|+$F-KZGJvADoJy+ z^mF5h2ynUCPcixrqX$71Gg@qC=(75nypB%taCdc4pxXzJ8!Yp}e}tsc zQW|UW1w1Njj0Yp<4~hfZKe-wYi@rcrq2MTwctnj3SXJ+XS?}a1@Jw55fL!G!Mw^aM z+&yMy{iS%pJec6!95Z!m#wN3dC%Ll$cNEiDSMxaZZlB1_%(RRgPxdOz4;fcPF8v*h zMOBwlxf;6-S2qodt~Yf*nRR0$HZoei$gf%}$%K$L{{@6cgxLTv`ovZrW*%HXFy)## zN<-;Rc5L7%E+9tRL17*C=pE!$M5q0*d|p!UC|IEJR}qKR6X$*^d1%qK>BlEgI;g_? z)<-QkzUx)|#j%Q#%%^fnT?siB4VThFDPFF7Kr?4pW!bd{_^463wptA zy_oYg5Q6zOT^&oS^WvP@(>s*XA+%Q1oZ26i_cxh`h$P>P35)Y?)k%+=K!dtimn)J5+!YmAg z3w!B_4jmm3G7M^GR1G{_w2T3eYI(2`T`nI^03CX=kzTb`9InqSUZMDJE{)t%Vd8tX zF4k?4@!~Vg*L$<86jRmL%p%QBhy-+8-$K)j(u;js!cje#Xo!ihiLAdX#(;vq&~b|Hu5r@w?5>G^ejO?ZeYwB|DTXHK4G}F!CGk1S zSY^Ib5S89DCb;w+A5qOP+SgA*IiO!lx%C-;yo?TdN#9m0I?9R;k-pf969}$GRSjIm znfsWjQ-hXgM3c5zoe_J?P)m;qwf5c)1XTRhcQf^`6}Jy`PpX7m?E0)#q`nWFh&92S zaFHkkOS4L_>C*~BV76LwuIKMhltPfObwbLJKlEB}_AqE}Nj*g^owSQ|%|yA1AZS!i z@IJ;9=HJNXZ1Iy0TX-sML4?Kym0WJ8zf~5GGi>7huGsu&Zxue;NojhZRF``{vpb{I zcJDPBti=^OPdnGznKTcK7iP$%rk2M>=fTk?uG@5>w0y`0icQWvrfi4S3&J&8g{kT= zZuUc@;$srV(!0LZky;w}HG!Qq{mM$_86Dmup$k<)HN5tW(?x)hbA=IzCWk)Seg z$QW@trl(@%smC}+9&6J47kqWpsL#q3b*M;o^e0+*q7Ju2n^6OoNtFRhD(%5z%T_<# z5mx=_!k1IkblEEt5ipJk#b7$LJ0`-|{N7G;Ewm;=H&xxvOrbYB`EjuY59m|HFLOgr z_nvYCy*Gp#TFQHrU-dIWxy|bWEphXBHRb5w4=L(M!J5uE0S?Si$e!z!P@IJq%R7$U zjXg#-sMe=CvQ)Xs5L#1%TtLgFs;hXCm0U!$4K&;3np7^u4p&y%Azs$F7*bhba=p1p zW$eOLs6@?6ZIN}qdz0TSO+Iha3Fub414QAjl~?Hkb-PS+Hgh_6#ZiCZ55t}Z)k}cJ zNfCbT+099cIz$A-)Vo2Qi_~MstAgIFTra{R020Ocrkh9ZHkC#&bmk2mf;y>`Q(PSG zxt0Icb+Aldqg0g2Bqn6Dz06><$hO|6IB}k_ExM19v;a4t0oQ&6KuQan@V3Cdb#fcX z*(8Z^-9AG#2l&a9gWVK?^IVd`ZLc|ES>0B@F3!F{>|Jxx+Q1Z>_Z4}Lf1xX+W){6G zmN(lK*EcnGUYd5Io6mZ2xPx_N=^gB0@6Fyo&8t4c98LxX4^A{l?sG$5Yu>068bn8@ z((2#4afip6hG32{BDR%3P)VizVnkZ18_X9 zmvU`wMJIMLcVXSZsy}!&dEgfqv{?S``6xDH$`QG(Ia@r9sXsr| z-F4@RnIXC;FFE;ySfoQ+!LGjB0*timM=NIVFbAG6XnL*h&F)71N+07Pwa0g|$q*Zl zse^>exf)#12jZnptKY4N&w#0#FfJJ;0SXbl!Nj?xFAAtQazi}s91`x0Dj32p%pGQY0Y ze9Q7_l}b;&SY$TNlj&j3+dlV|!VMv3F2m`War0fqIu%yE4=icOdUReAEsILKXURz^ z+DP!a*uo{IfJLPkcDHLW>f(TSs}2viD(haYV#!oQ*BZ+E*?P0>-7UUG-=3Qa?*t`s zD&r|tm&lEtyZ5Dy0`Ak#Cl~MJC;X9Ukmc?d{lfJ~{Gx5oT z+F53fB+E?9t1yq9`pF?+I%3ip8=ge1EtDH>#&*V^1k%{_T8ve8ra#BB#8qD(b1|EqeUS+zV3aJyx*x(ii9dv$pXNgwD#VNEwJUwNJ5>yaK~x?lx^Z_ zVHgONJUg=%>Rfr9Xkhxi_BHX|nh#AK0Iit2#J0z5^XebYhwKBu*A2D*>KIua$sdiR zO<9cyx^qV3v+&}8?e27p>6_zT(dwt2saRR62~qh*j$y!kL41F+Pkn97q1*Sw{Mbh* zB-0^~``k?SeU&%(ag(mZxFsCUpR#qv?%ap$V=5yWZJ|pT@Wc3m2_{{!;Szk)yXPar zyt5?NTdpJFx6cLeRQ>!=VDiv(2j9}QI-W42P<+7`w19;q@Y220-cMVoa(P;Np3h0v zjYrsW+EPVZtJh-v!}^e>c*|eC3jo6MXRg?eR&}@er|BY9m7pRCtueOV>ZYDv3@E0; zR-EU-CB&Ub)UAL4_`H~NNb=otI`LAGiXNT#lX^FoJq5v2Ad?z(^P0=I1&oYnUH7sJ zr;hEi7RVANPZ9A5w=KQb@vC@zvVE*HWXdARke^ z1F$R!osC@z+gF_`fj4Ni-r2F64Bl>+uklN zlcr&F@?6(?Em$ugZa}A@f~mBNMd45Ch4YgbLY2rGzESZ+sdCXFUeHMm#_5`!bFut% z1VfUJ{$w|q4SmH8D@>(i#jc+w+3Pbyvh5^k!=mXk7xJ)1_adL~Zf(xc5EemBBE!WE z+a<)M#2^=PW44b7k4)NRlhF!in}NjsZ;Rd>VHs>6|4_q~Ul&J$j6MHv2Kkcd|JP=9hXcjNJMh=iZNu7$&J? zV!mA&i*XXx4Hb2U;&yie_5_)vOm13ePVsKA5HG7x$w_}4eRU# zKVIc}Ud&4f|8r+;vO}P&yL{n;n`rX<^F;VV>W+C)T5FcbuykLQB8<+4d_j zyM0r*c#>3nG05h?E4EE)V!wJ`nVGvA2yr=FQ#XwEcTd(H^40IoqpG=Fc(+2Mff%+H zwvN$TAikyGC1Gb^0*3d+eJ9;Wv+4W23n~XcRlsRV=IqUuj(=D@TCg;2yMt%edRo2| zCrCWRgO=nbjTBk&;^>A#Y;4B$VK1&Kb1{6^?ub~Pg+5piIb;wdWCv$KaqtF1#H}cN zg?VkPRLYmtT(mjv>itR2$e=f{S*(G5%t)s4@?Ep1E`V`knm9bzHWl99s+>D5zUGrz zXshdcWoEaZA^@^v9O7|aEYvcQRiueC?s+{qk3n-cKZ9XyLf^(h&x!|!xR8OYW`(`h zPUqb8WQFiT5+s$x;|O(Lfi6vKSd`j#tS`=ba*PS(M-t-A9ETl|$V|lQ$_AfH73DIk zw=KW{IoyQpVW1ZQ^?5?KV*JJBitUL-imTn3z28N#cT;I=k01Bms47Mx?_?*-=CmQ8 zy=M+D+AVfEAe*JVg`FOLf2(>9e9PBZ`L_elgq@6z^}G?^bGV{6;cRgErwJlgw*Wj- zpkAqZ1Z!lo@?Qe*wv;RQwk7X*Vv~9GrMJ4SoO(K#85jPmHpzzGgfasqk`^PDG(Tp; zWdC?e^7hXMe_@LB5eE`aFM_J9F@sh%XXUd)qMBbPH2~7TP?(Na@FsdR?=5zh+UD=< zV`luG0WR2!AXEHztSN+e!d$`}+#{>ZjCy)7Q2&d!7f(1tN zD>8AHzAQiXH#_lq{&g0velW?s2u}R9&vS;p#F!0WK`WE*U3%vSVOZe}?NK$gm^uLL z1?%SlbzSY%^I-B5IctXxr$ukp6o~21|Ta(;vcgd3DL#M@iantH=&=MGIj6L!1 zWL^_S+s-YIz@zheQHt8b+=l(!WIz_HRL}nG>r$+{{ya+6LPHzi5~~e~os;AvoA2VZLjt?A*PeoEy?)3)FUrM0%&S-|e z!=YV|)7Z_Zy)PPOWWre2F`k-nHK6xc6q49|F-~aSU%klz9Vt4s@na2%nVud=pgV_^ z9a&nmXWRecry!eC{U*Wwlpda-dXxHSBVR&f)7r18k?#e42WA1t?WRV+mFm64Z$fn1 zFxC0fTeJZX-LTzj;34paHFWvYdEKk?KJTt_$RYyRoEG=i>UdBGy|uKwj`bd;QMv`y z|L`_N`g`UZJuetwsmj#3pp(FymGxVA)vJ}QL}#dyR*@sjz{mwndy#;ZEEK13aygIv z+2~nQ26lk>0ynhLAe`+Ir}$*E!3jE^l%D3mYp#)8VX09Zn&&5ey1-|tj&B$|;M_tF z2S~N8dA#FzsRd{b?MI4>h9|^p`3fCpLn8347!JE#&vX)>2lWJw-+rW|SE{MzkoLU7 ze+%w2Jdm2jqV}RGx517$T9txH|I=kL9K8#InktBgIR}qY)widrZ-OgWsyEwkAhd&S z+s;{8OIrbNI$H-++t*9-?W&#m;W3D8oU^(eb%GXc1cjqag`Ve?d^-a?mbh??O- zQwXzmOv?So#o1#3J?**lP6?~b6R4*6vU6~OTRw6p`-T_h04EeLW^ zXWfTF*>>sk&>4CNW$Jfn7?+!?Fbj$WXW3U(Zlw(`bK+lytMoPvo~X1(oHMqGj5PtA z&z6ZV{#b_Vu;gl>N>IZ*R{V1d~W?;7CD~%$5I?# zasg4hrufPEET{+9*eP&8?tGww8L;v!A7iX}%tQ|N2*$iHEYZ01Lg)C+>s_;gnVCyv z*0>v6qP-E+qvwXyv$D!MG9~@$(KM3Xp;agUK_E4ohhW|XK(74wXeRjEo$kKk(+p< z=XUWYKtHQpk~(11bekGm>10Chf*UJ%IhChs;g~wmHSS)2%ekxjIpGN_40UL{TtzDT z(k4>XDSw3f(&#A1PX(k`oiCvAcp^SB8{(f-Fq&}w`#`gOo04_AQmmGVcxz~T3C*rc z-BQWOpt;Sd+S%9qg8s9e24 zv)+X|itX`DbRb}WpDQwjiL+7|CUT(8F=D514}$47xPxEY?UlU1B%@|Ur>x3EPw)Sg zuj@K&F9FzmoqZ;m6@qlEb$+<(lo7@hV~~Z%gAGV3lm+vG%}zX9D2!u7(q&DtEyjdF zqpKp5OdfI(pT0E()>C{;u7+|>tUJUtg6owzp5DN}!)3Y1DO>7JP!yFbGu-`h$5EcN z`OYKG`JAI)jY+RjemkH{!<%y+fm$3x9-=#T$fYIB)fp}zEGP_~vLt@D=fA8Hy3{bF zEZVNLE@9`3KR01zyH2TTlDzt(m$5f{Om-ZXkH8Y5aVd?18cXz0sjaB};;DvWaS?V@ zrybTZK$c7E8F;>2>D|FT&KMD~n7Y#Um*E9Zc<=_0!SxN=^uZH1Jm6)D?G?*>oz^Ka z)`t8T%l6J7Z1UrVog{ze6&EYF%QA!d80&nk&5eZuFD6qQU6|T0oP--15?^(TYpBQx zx4h~fsucIw@h27YyhKaKV)FTw^dggoIRO-CCoWSWpm%55mh^{z3XZ&`K9^hVY$&IR zT1!_wT?OFee^6@BPn^s+)*?{pZ&AM$Q~&v)c3l9vyZd;Ap>;e>M*4%Ucv?gi=6wM?hcc;!K`U+-$={cbdxQ0az07p>1RWhFCl z0~1~EDE8G929LXPXULLkKC5OBt#f!ARc+CHZk7F&*QUN!?XoGgwf6tl-kXO*{r>-> zB&jT8DLbQgi^?)dA%;m&iD;F5n}p2R$zaA>$X3agt)x`;ZS4CpWes5%`-C(GlYMNz zd(`{$`F_uJ&cEk6f1Li1%WJ#0=lXcu&)Y6>jn(>HzN8CqDf)49SRhk8E+Mrw8GvT^hti{NZ!RbBU5vgYtdJDM5C zI}keFbt|mKfV_8LZrKS6;p2;=$Bn-JdKhsi+Xt zYYXg8sly?aHDI1uXDRRI)A}>uLc`t7NjLmFX|whn3xkuQ~2>UWPBq4hM2h!%Am3 zt+?YCx~q^qZVYsg;v;r;Vc%6y#sAqOXr-Zr(l_(SE&W!KZ z9a$W#J$G}ixzWn;_GhunRwQ;$R}GYL<+fAOX|$_3BT!FV7wX9fwrqH6Js;V&F4 zXI}et-S)zJz{UaO9akO-6vRl7c4lL2Y zJzW&DRpn59alz+JVf;^4tLCxelAtopR!shMzW~EKyGhg&S3o2=uhu(ko|&xN5D3=3 zD%+kCYqT*j*R4N{9(kSoX+CWsu%bLJIwt1B&YHjG_-DD+o@f^&v8rTWXJ9M?DFAEO zS~5pG>0DsSiT6pBNGr-DAAR6J!Bx0=RBMx$p7p{B=0-(ml18DqiDaK*vu+={Vs-_i z$RxLN;?1DkF`5!_>&owyVzKG#q@nx3`(Yj$a~!BFd;8aqz1DSbEcJu4N&lnUrT{jO z3!*hPOY4<>32RU|A(!JUHtC5AH^t-&of(d;Qy^0Ikw2jCo7GfOnXw6TO+FJ5oWN$g zr{o4NlZj4M8FJx07e3H%RwCqT!rj5CY6cAO{c_Ri00uzv{eiNwa{cERDbv&|J$KU% z*LCtgqJ@-zG}j+6060@)5BQvJw|y;1{?=SA;rP2FR3@L~a$7|2xuIl;WMj~JtrXkX zQ$Iek=gaxtx^uZ5rBKy%;!>rEUwoxV{PpHq06iI^Q%^j z#)^+bQezHY?2B@D2Ax~_?MGEy__dIO{&(g40nEH5OwQA_J6!%c=EZT0+#@jG^sp-j zj#2jzcokXAaPkZ!ijUjp$lXlI+ov`EG)zjo-^|R>_D3g+Dr$E1x6GmVEZZOI2q|4m z|Z5)3n>|~LIDu3ut7fHli_dPg?j}HQoW~_#G6!3| z&Dyt5&PqbGi$3#LO1W%;L`7_(W6prUCG7i88dCf9*H#6-yAr4+HuW(;ke{6f(Z$9> zTsj~BwW5N3UB8ybLAW0T@*i9q+3r%q@mfVM4>tiI$JBGY+7*EQoxlqhTRCtVR+I4D zb}~x)g{RBNlv8`@;qi+8PA=E`^CvaQEBIn#b1p_=JrWxprRb1-(Kke21@8zUh`Q?V zk1oyoa|v>99#;G%?72xB@WbG+Z$AQ%scv4SzrBO&(SRDMYP#2HaDjDv)?45SO-3~9 zw0c?$cXq^Iv%Uz#Zhm1p;Mn$nh5SEVEzr1s{JrZeqx*x9Pdg5*R_Em$iXf2|BnVx= z$oA_Q9Bu~Q+d(OAts<9NKWURZiA)M$o&36eUrsag#`p^%k$PH52j}H(rmEH6Ze4p< zMri6ZL0zneU3J5|<$c~8s(+~nfX5Fsfog0I12G^nHLu>&ORfE>hLo?#vqrvC$SD*D zU}-`b+%sZvM%Ac&F*vchVe)9)6K2Iu&Z7?-P&)IZpd zpBv)C(Vc97*IzmN@S8{>2urVr^nKt-JFKzS#GGOG?Ywv@xZ9uXcI?Z?$cTZ=Tagvk z4nGZws}9AGTJ=$0r;<`4nQ{J0U%~-R%yz_}_~6!YBQakvmBshcVp#SC%@@(PYyG`4 z?j0WUYrfGb^mv~AuD0UbX=>TZxk@s{c&2H6m3%*ag9!ci`#?43@BvtAHRJjwA#7V@+_+-X6aO^3TG&*_+O%{wbZVQ^}6302>L_s-Q^)5A2B>*EcE^ z9|EJ3U>lr~<&a$Tfjc*N2GaGbDciUKr0)4fw&ot&xLys_=SjK?1+`fQ9@qf7L5{ik z$#E5pPe+BXv&T^)SG4Z#LSUfzIr*Nv z??K$Qn*unL%@k!1%T)wZrU`Q72w6?mRlMh^4FG#RxtyCAv&GA%XNvZ=b3Aojpw(jz zcbs8>IZ`VXs@PJZRV(G(CVth!i@rfY_Q>5GY$$Rt^Zf?wN9_-!xALc9z^wr18o#IX z2mlT=^U0R|-P~wK*3#3fE4Eu)x*9!LEjhH^_Bv}GAy_Q|KZm|NzTya=p&(l{z`U0Q z7{mr3zzi_=FCqKO7;d4?j?$1TPSKeUduX$BdadwkY*6M-9^6C`XT3r&1=tIXB&i={ z4j&ZSH*!`{RQZ8N>Cd-K<0;d9hkrpz>Don)6kh8VeOA;I$Yy#qX)Kw|#p0NpSQWBq z1A!etDl0tc;H)T!D{}O;bF?(rm}s?JX`znewe0IZJ(k@c%$aZ)w^=)}@RMV|{`T4- zpv{1Q0gJMbJrLWT*Z_(U4{dsZj7e2iS6VOKol-z9*Vp3)*vH@TWm{GEOzpwKve;{c z3Z;{9{Q+kzekLuM*?A}}mG`l2DbYVfE^RyFN>M)woK{^a1->_>2NBKMMT5W^2dIV0 zdyG`Gh)U8xjKY3(xCnM_Tvczh5-;c@#VTOo6X-Pit!%u_oB!!szWdh9<-;dl?+zz0 z9_o4Z-oA5{iygDWFCq@*90LmdJV1(_Rjq&jm@PFM95Cb?zy9wswPSPAdlh=0HOl%Y zR8tvN69bmx!5WW-+>zH>t9IAB4n~V^KD~{A%!(*Ezg0lQivKLpF%ruWs`!UbK^A(C zBj;M#BMxy_Q=Yk$0tJeEW4Wj3AoIUV(8;t4fS|KRYL9+g+wy*s$mOl4sDJH9~`RoS9MFS6!7*{k?1| zjZ%B({yD403VXXsACa{HHupeRIbdK3y9~Pz6#D=>1(czCA#h(w-Q<~ic39i5#FRcM zpi1T#jDO7#{Wl1WOfgbPFgT)BxdMMF@rCVSe`do{3w8beP|2j5api-zig=r@{tvAl z^H=Ev*|LK4X2EU*E@hN_*7muE)REH~|MzzuW9YgVV6B)KO&ny<%0v7pl%KcW9or3I z__I3@JDXW*V9?h7w(MeBuNuRL;AOqYc;}5O%}j9CR4~b*>U<@|bp9YC$F)7%2$K4{ zf^*8Ks?^cXi;UqCLFXE5N(}X<(wBG}>C* z*0k(Sld<#}e0;=6Oy~NqQ%2*{D|(SoD9&vv!LoU2<~+vI{XK&`0^By%x?f#a#lHf` z(A1X!W{`hsL&Yx)uHS&H<#D!Bp4v?wTe)E!tfAJq>kfBoM(T%6Mcd8 zQV~4ky{Kn<*20F|GEepy8UvLCYnX8m-Gl!z%i~1l=_FU+Zrw;WTJ(j8DmuC<=p9On zNhr0@E_Hjb5vqUrL>7nMd+chnMuqo`;f|aJ3KCF?+K9?w`1zGCJa3ywAUOk>&Wes7w zoKqoN5gR3>%1DOo3iDzkJFN0drlXgUql{+eQHOZGId4nTQpIGcgIz5^&tuoVHfYzs z?Jth9bze9y-w=b0eZtmL?T!3LBh!!aE$^$n@M$B<5e17{zuCd^2Qos&AH!w2GebA2pIKgI}Gh*oKMcPHp1SLu#nSvO)Xtp4@VrfC7{eTVriCGqP~cKZuT zuV4r`kf`cqiQvzkOD$K^F~~a}o?2oU5<4{mQcECt83!cM$=}n7I#xHZd~d$!Wp58K za(`%<#a(`jprTO?=AR%6FfNNp)?eQ|7RDC<& z{ouqCIlO7_%iOzvIn@P*0ZvB@c>m9i4-p~_mmz9`yN9O3H`?Sax60jf{!2Jv@D)EK zIP=TW&kkvd}hbJwCv7CRv`#z;0O#PgmtRCKfOQRBLw*Lry7o!68CU^IcFM;U_FPxYag+gH*PnUoP?LETUT?++1XqKysK1s2+`gTzx|9Lfu~47N*o8-mSdm5yX!X+hmc z1@}j$BTr6)+l%;0@!Jc*2oB%%IA0;7O4nyy!pKGjKqK zA=WEn_j8YTvsR2i1`MjVLY6u71bqF$?xDwkP$1&v8ug!~5V!eNINjF61i4M*;In7U zf6PEW)?1JP>B_|ILb{9-zm%xo?;PK4;(3eSmKes`9B?o0+G&&e*LSwxe_yHjm;s{~ zST=K%8d$;O{u}cUz}s}f3UlTx-Z*N+taDMqLsjEdvD@*HXp-rK6keqo6Qpvvt2gXRi2PGw{S0obU#YuONF( zg$wcKGM-S|Q}<{t>B4x~sXMQ)L;?Ks(RzXPF10QNRHA!*eV7k?)97#r8#j-W3{>^e z-Id>u{`&UTo#$6YfRVd& zm#Z=GNWW>EsW~BBT&C-~zTzK>l)%GfpyoC6|3Kw5<%ZwQxM`7{2&KWfb!H0N&8X>;uVwicH&jQfS6UZY zV&+&Zwl0D!l*orl97bl=&LjO6hMFX8e*#?G(x|-@y%;a)gM6nXefnUY0{!R)MR}~Kzw1mWHMhSFM|_i=Zp%qlY4|Fcnq+Ex+~|VE zMG2#-Irm(4Hs3AEcs|e3f>d^wH)qxgZQ2j6j=1V6ies0$WL4PM#^j?R;q_!xcSlpLNJZB}Xfg7SC=yZQta`JIJ9COhNRh5WTJhHVW?wWPj|Oq~u(%^=eA zHy@On!$-AdaN!&0{}~=lfki^-Y3)ilKmsu9FLC|)Qg-XL;^Q33DyXAMU1d25t)VUZ z&G0a$qC=e5>q=)hYAroruvU0r=WjV9xv|X0d#U-M?$G0;Z|}4$P^pfxn7cjq|Jy z5s{9r*3(P+Nmn~>c_|KWRRNBNEw83=@d7CQL-PW7F;(7y^n zHoMtL#S$XKD0E%i%7AROt@hl7p00e~%CX?Z`b8Gng+R0_;=3lo5C7&y3XIOZ%8p(3 zkyGdsu;5S~T}!G{qiyCv@?isJq51_^4CO=g@1+uEmWhjElw`t;`^aKbWi368s|^Y_ zZ&N(28byT*P9A;eT^%owuS1_O8e6xj?tO1Q;}IW+slL-=|8luK2YX@B!i0jV`JBLp z+`&}G6PmZ`i%E;g@n#gXalY78SIT_&Pcd8T>AI;8oYq#=^Xuc=ev{0vqAt9c4pTx= z2xq5^Qs(7nhNR~NrkqmdXWcq%R+%GHyO z>X8e9<#6+&_7$ne;S#A;gXz(EF*(`g z(#MTp@$`Q2zB->|w^W5}n%21vhg?C_tQ6-yL2Q!|v9^=`%ysG!<@+5RF*TZ&L;MKQZj}(2i+crnv818Rx4!+;B48r zu607j8sG9p$%(%h+4REI9%L6R3A-NsHQtw=;oH)jTy47=SG{z+f3;$C%OMW;mebL9 zgJe2t&juMWmyHxj$^##6KXKSTyw~*c@FaaaXi{$fw^-=r7+cQ#RkhzQw#Wz1>`Ps1 zlr$NQ)`jP5*L;gbZ1o`Av5pl~{1$;Z;P9UEX*P)|qU%(%h=bGC$Zy{r$2doWZ?Q-= z4kv7MCpL$4VUcl)X3qS8O>%FOfG3VgKq^K4CnD%RXnTE!>%QfksTL{Inwss*nnCju z7i+4wvK`B+&B2+N6|*ei{eVv>-oGkPv!)s6x{rBek%e1QFlr(6fZsIR?2m)wmBQ5R z@5|ov-lL?+ecaUT)SY!`|0+1TjnWMZ2bHR6T>Y!RGIQr#IxZffZ8ACdd3L z)T%KrQQG7?-L@DsS$7p1wXGSwifUSCb62cl&3b#z{I|E4SEB?vgod~=xZ06ftHi_} zhL|*MvlKlM_!h6{`|FNr2g?#|w5Qsu+9t*M3enO>&SUL&^J3GF`V@JhWC}FiX0PlL zJB??C`ywN!X8k{~H116*#)ol+`iz|8_1r=l#>dJ_b;Sp<{#DOIB?_@Q4pa_8XPQ+( zZG5O=xElPWjqk%O&n)<)~8mHAc&t%Zr)W<2W@PQtt=%TI01`|fPp!?1{*_0EcF zP`S-54rdv@v)Q@bwk&q`!I$ktE&`{9E_ei{b4I^muC!s zZtaRB`N=skT9avcUDXWsXzFXT!?l&@PhZCkZptLGv`DQ2!mlIY`c_v+U ztG;X@V`qA&w7TT#Y9#kJRRs5_idkwi&y-eC3$s82ILcT>WxvEfL=k0<6W_l{!}bVp zdplW7Tb!L=_Zm$a|Iod-T+JLz|F!P*sBHSN-XLOLd&J=?zci5j;-%Xf3AN^ZJQ7fq=?s5ybfbGF$ZJ zPDTAzIk~0Hg>xKROfA9P)b$DDxKeZOGjMZw?*r^DHVx5EMckvk;KOWI#qjy0x@b}7 znuYeDX$sc$sfc-};`S8BSfU_kPIt@VWu3f$lPv^AwG>!2_Cdl@Q%OOJ<7T@Mz9F5o zSgfeQL{Q$l_s^{jDidLe(+LK2?q#zdM9BqKt`bh8*DJw%I4_))SzUkc~ul zmRC{adN2YP8cK9`-!>SKu?9C+v3mmTVDE%9PIKUwH*X4TF%1Sd)~cG%C(1ANF1mLn zoSXBkrtt}vP3x5J8md=O*a-N>IgO*Wm4GF^VF02C(f)85+MT1N4uh@%W6C0_gi>+u zYR}ItDTxLhDp6IAwQ{}-K4kD8l(y+4PSA^3fJdr_mrS4ck@NlSQ=xj`wL>r_0Su;U zvgyX@BsQ|q&vgv2Vn|hUpIgGK?2D$LvtGiBww;g57r$Q{fh&QbbQS1?=shWf`~7x? zj;~LAaxg~3%JqYta~T=VqvUm~U8@Bh(!*58q{w>~MBZP*XLEjO(QczW`6=@DzvW$ziGYN6x1S zpCM;bq9EecLGQwfh!M#f*dG=}#D)@Y=ih zR35i>IaD8QbtwDkh}#|F;_ucuIYt}z-#QhqeJSkowIVNy@u=|j*65BZ*l+`7JQwtn zr%hyJd!+>A`}q=;-5=p|cedxZWj8m1)qaRG0<9qKn1HflMIXgAGahg78 zzt8YF@`ymFr#;4<_3>S^(Y~d2wW!=>$ZotJ$@Zi*RY?ovT*6$N_#}h12|fsE1I6q^ zlN$u(J{_*B!zTutXbERUt7pK-U`5mkgqd!E)YURv5-o}Sht*1*wSjag`u)mpsj`rS)Ik%Hk6 zpAT(f^HcAJ@zlN6UdS-FC77)ZKd_NkrK>X>q*n?dm-YXf}Rosd|Irk3}Xm}hbK z?PFE!CcB^fg!Sc^@y+2@Ue8ur66ORL{n*!aP|v^3Xw8RMEXfzojTrOgNrsZR=e};cUfAm&22(vJ$_lGn^SRr*G+|-VL=6@nM)zR1}Z(YQ)5ead;fTtghYmy}=7B5ix+BMU^V_rqE}a8C(lGzZr}$idMgX9$<$)WYt~Uda z$@U8Px`V{W#=2+!=)c>86aQb2muWrlk*SY3(7nHhXLUk^ZRCrEw+be*=41XOa$qWE zV~^wtYiVqnm>G6Oxz)q+f^6Vct;b18Ekg4Lau2=G#HTw7gBN>avNPd=HiOUMVQE-k zkmv`kZi{9xP294|VGn;7MC)lj@rtE1625jufw%d#17z0YV{5Ga7V_n_q6q~W{v<+{ z%`W^sW{XA3C>RatkvW;TzGN-ryptf1(StZ;>bk%0MN06p=J2)4hCfr$ii?$0CPFaM za^qFJ!W&VG)dVqEt(yf!GeHZo#j^!cq+OgTYi3?nx{vmY zjIm|w$>*rF5Q`G&<}Gw&YUQE&gOAKg@Sm9SLRwamU92`eP3zCLq_gSR%3#Pv!Q{_&;wJHw$96DV|tpI zEQ_=&Ny#B?DE&}om2zH(LOq1i7G|kxVgoI$GR3bOOnK%x{5U=(T#p~1%6uuret_j+ zvVzGb4p|10gY`bKVb=Im>wMWxfn#>Q2K>M3lovHzf&1dtu z+e?<~VY$B72zZ64#(v2oAz+g33wpx^TT!A%4fL)%pX(^0^;c9A0oR_{Ou7gis&$>F zsLQ7CR9;O!1u25(L5H;F0on@HJe&%%FjZ)`+7K*hJ(??>j_Kd`%9<=7E3#DNTKCM0ioD=R!x%mk9<*!jbQro&Pt`x z$Ri?G3)8+eo6#FRtpn3_IIK4#u;{I8!BCPP zo$AhdY{mmGx<)F6Oq5H13|n9Op>XZ$fI(!*k+!QF@%$y7^mnETdPOjX(#WR`1Q74Z zI*_QSO4e&O;0(Qs@C^JqF%pw*GNhe^KN3OXJ#*q{GWC*3LZmfCuVs03&uA&ug9G(i z25L9r&hkc$A`+*WdJJux#5LBr!k2abtUj$c3OA%jQ$s@Y#JbTK{ef$nq_cenU#X)RJb{~ z!i^uG^BCi9f0pmKOv)3BRgHIS1w}6SRUFz1NvtSdEDqHiM%BwLWfYmFY88p4~m_LU$JFQBUl?k*%j*eL!0CT0mj zs_oB|I#nz{bn8k1FU2-B+k{2fXbC@_C`Ff$-2*V!N}LC#m_>}qJ}3c#VwQX?GC=|+ zhi*9AO$&7w>CEr27wev3L3yk4-U(*=6@sjgiKae8prKSHq}Z}W@;Xzi*#<=YD=jB(JNlh5J$wBGYYyfh$a=I-~Jj30nR?9AZj4O!A|JW&3c9;{f z997H8p>a)0;Q8-CN-^6pJCSevxiEv(K|kvvSWYk+k86xgef2AR@tcC9v361?E?
    *0)+ks?sHZQ6r;#B@p%#z(K@*>818}=9<-n|WDG}A znq-Z;}3M*YZ!rR5yOM>$tim39`O zgfG4>ng9Kp*iou$L1?jewQ4>&Vj~S9z1^%-VE!T5MU!|92v2Du82>N(IQAtF**yI- zOvD3pZXe_~8?*RUclK8?`m%EO&8Phjc^z%?CrRhlNNNO(Nr``VX7Hppw$T3&#zV(Y zvAKngAu2FM%5U7u*%?veX}ZF&;FX>SxLZ-m6XFvB{X(6nO6}Xe=>vkHKFxPU>=QcA zGQ|pMGvf=bUnuFik{XKyTBS+!Gb@CA5eqSiD73YBExxUUB#}Hh054KKRs6|h$3V6R zO?vLQ(c&@SrQr>9>C)lG<09tQUN*}#n>C9z=Xi?YmRL_lImhKoJF1SvI=`gU_8pE+ zrxwxr>6T(dgHU{v-7JK(_hpeut+kmVd_v(JLORO2Pz@iwZDOM~XhXT)U2e$VBkgSV zlKu{t-n0>8UD#2XZJYke}2CTvK{LRu9@%|`Q*UNzr4#}IWz&wu0%UNr7y+jm*S zgxfRT5zE~2;7z@U0pV=m!Oa7RX@)muMXgmfTjb-y6d4||J(D>$| zT^KhgkNDrav|j#fMIP~DICq$dj08F#7;La_@?U>7?d)3E!3}K?e=uC3f{UV;@*F>0 zeRSL?6qEu5S%nZUK=lEBZ1!&I01a5AAQlF&H{+7gQO2b~j$N1e7w~exrD7l;0MrKC zix@65m|2E54H!GX2M6AC=s(fNv~G;Zggibmp;<y5a&)(lP*_rLwj3_H590JUN{8%fMdNwcr zFcBWx0AD)`nF&phT0V3~#%%UMrFEGf1`n&n3sIXg+9hf6#A;>-ZIpN(W{7{QfXpP= znV)pG%n{zQ40DhtS@k67J0!{2MlGugw^$@d!&ctGu65%G>-e0~;mQ$bI9jbmUAh03 zLTg>Qs{T)8kGkG+7fk%9_s|K|r2`TX)?xg-*)QFu2a~DE=?m)Ou~g=!K&Ew_LbiK8 z&zZB{iAqa$_{Tiv&d<88_k7K79}2dyH=dy2@5$jOc9it6h>cpXPSYZ|;ZKc>;LnTa z9XBATbt*VlBie0te*Z!$=aE@ob_+IP;IetDivwJV4@-cVneg&uaOHTCqC)xJ2_h!k zQ{KU$`Ko={u*C^$Z(T{dWca+$RTR8QnJm^%burtRD0nVT;Eff5k(9sT)6dC&30WvmrML5pl9b+Y*|MjA@=C{zRc`%ssCNpZ)9BdeKamNc>W zY+7{*_4qm>pe_Yhr z(xE#Wf{8@&SA2w1_@iccbl$RrY9zu;1?BtXBdDp6eOYwz;zS#!rHSK*+^8%eFQ-!J z5y=X9qNR|=LEKF7%Z>2Go;A$!S(w~LYi#0GEBd22f2wwwmmELdmnOIWWf2Ph1Vi(PP5FgPQ8WY5581D? zQtd@0b|9xH$oBC3IY-GsmLqC!P!i!io=(k^mdR|+ykZ7z1P=vHiFR=})no}dJar_L zC(b(GT~b%<3o%~#h~^*o(%*`bAOT(m zv1pj(mO^$HFp!#!Dc-eVmld8iO-n91MOsz8wD$87G+zka6r5^^L(hJ~h95TgfM{jL z*I-(iT2HdIY8gREhc5ea#~smNP(Q_-B1!Rkc;%-j$^~FdztN%*$kRQu3JQ^D@jO!` zV#vw>RcmMtAW>muUkFN`$T~q(&;Ayx_0Ut%Bu@*GT+T*&mcD$>*l0HuS=ZrAwr|0A z2N7!nE(WP3j{eILt&+~ET^eARqx zq%X2q(Ze=sSTUA}5Jxx~XSpKM#K5sdP~+I;u(H(@?*cuMJf`kh+T~f|pmk)R3>(N* z3_XWT;Eld1(wp9>Ern8OL{Bng%%&CCif1JyTUy;Nvr2{Sc@j~eAHj;uyAk#lLYcMuJ5d913}Kr2#(u&XIR?N%&~Ivd6$N%&FLeykWgg~cB58Q@>DOhyK} zWyu3u+w;3Q&~qJcR*MZdM;aB(%{oTqBel7p)W(Se<>QI}nWM=;;SBl&yoi4&Ax$^Y`vkKm0KuS_vvFK~0z?SrgpjVz~Z8e5$w|GI<;K~+_IJkYx?t-Wd zs`KoAEc4cEd|;`XEMJzfZ(m?hDViS!rA!WzdP)`d053Aca~kV|`zK=B*9X@;{qT4z z)3gdjtK0K%>f+u>(#q0zweSAcs9<+vNbT8;#1alvg01mwb(5%8?zGzgl;Tn%pmmr- z6I}gMte!@^^SBThJvoutPLTYicinv=+Il6ZA1b1VF5ByFRiQMpM?}Rtz?!fXP8&3k zg;0i|T1IMd%+0KJCa|k|TiR&_{@J8CU+J4K>KL}{86JvGl*u#^xd`u=T!|4gu4qHgs(lqv z^qDFqh%~qm;W zl?w(Bq3C2m7GqhzDC#@z7_f!OePQY-m<_|!%6LoiN7H5n1+Kc$=EKsY(&$$c$6{{@ z#I-iv&EXTFFqK%V;!UhtuZWX6;l|?Qzz;1TTa<)tE#j@cFI<_05X5o4vx)KPIy)2a z6@P1F@;XWYOLmS7C5YkhJ16_68laQ;YCi_l3e=XMYIw86U}cmzA&kt0D+zL zi0LD^^&QTYw5B%}m2QE_3DF!3`fs^4_`#jsb#^8odm}TOX?Fo^`MXj!sF5wLFA~W(IZVb-NV?)m*)OysVFZ zLX;FwHon=xo2;BV6`@s$hW8O;H=bkgaBXv4r#70KBwGbRs7L~#dp6wWO1Ws#_C zAyv8|QfkxS3VaHs7)v#HQ7YXRIC)H_e~!-~N)LrxWa=ZY4Ep45|2wC9l}4|L7S67J zEe&veMOrT6xcNcgXh_6y4;3pcdc%MVX=4Hb2HbiDo7UyXSG#dJwTM;tSBg_YUdgNsPM>B?)c#rw6MsN3!&j zl4H5EJ)aMVeLWYD{5qYD>Gx~_tKAZzpuIt%jjYwe!rDr!Yo%I!R#iup5j(_-%n;8B zd<1*~X)M~k343XMB|U7TkxJZ0XU-y!_T)&~sh|yq?wc7dOoHqR@}ZVanZXqi1GAxh zj#*_gevW3UL;M!1;!@mEHZSqP8!s3^gNH&py%`+bR-^2P=oy#zLZ-rJ*aogSaD@6??a^;*70inu-?KK=YHHm`is-!PdUZRyEx1n({t% z!>FQg+b0$+8HdlDis-$X6~|XhztkO&cJC!#$SE1w?0!UpT!)E|Uo5C$>^jkTnSxw? z4LKy%R(Q@M4QzmUq`IIfJf9!Ly%n|^#@%%Z)PVhbR4xCqma3$o7W*^(+DOQP4`_+M z-kEa^B?n`^(`<8ZF9*bdG` z6`R$Neg@w;q9LsPjq5<#nWazWPx*T6*=4R4(cJ{M+j=btPlpqnG%H(naX|%rV|)&Y ze@Z%~?_651^JCmUL-Ol`XDnCVSq`-Y2RUIL&4oBkQW2cc&CMNweWUKq@k*=tTNtv< z`_xl<9XKVOpPuJ*;tQi_GvzYK)Xt+Di}I<4x$E$OeAxV_gMQGK3H(V(Qg#2E%8yk% zhNHL5VYwoL(I+FeE?>ZpG9jDfn7>>d{5U;-zyhe^P;^WEG+YzZl_wNL-Zvx1X9i6Y7#@+OOxZVp#ovzWygl-OYbem-m^W; zO{Il8Z4pyHP~sk7N^X>&@<5L-C+PTM1#x0Na zEFM{?A`eKNHD79993UJwG_+MzYgLD$kR=2IBJESGN`{HhoY(eeHz8{i-lvG2L{wWv z-=eTB8^I45(OJ<=o$F|sslG8etHNIjNBurLkkMX(wEXHgKWTTNrlVGk=2QMMvd;DX z)SP0tyGaQvTB@ego^9|feAxGKjnh3mWHng|H*8BP>DWT8tzfk5YL>U-?IazNJ?%<8 zhN23?JblCTUHfznu*@k**t2V#JJLb}gL7CH`*U%iv8PqQeXN6}{terofmqJDYJ>6D9SllD;wa3-ZtnSn}4OW>Sl-JjlW+3HpE3+J}im?4s zcxkZXiB;8|YQ_$I&F%Ws*k z7L#8R6p!y{5aNAwk*cj@krd4A>V3*l>}^$@1(NkU7u5@T_m||9?T+7qV%8=^Q5Fv; z_Qz%PN9VSk{Wi7x42BKV-1>giybfMSuUyEh%&W-SuE^f@7R~ zg^q@dj~|iy5kqdova^ranh2t22FM>hE4+i0iY!Mnu=Xr^ zz*5}!bo&!gYW)Cz0`d}917(2BzqbaSSR2Du0Dm6nebcdA9j(vpc$4{#mI@qpqDwt|r7nJg4}^0m8{7=}4R zUa}9=9m(K;EV@Xc_GRr28nXKwX7@SP$%(+Z1Lr~EP?>d59WiroIDq&^JO{Ttu6$Z50n?{hlmYyJGN|ut zQhhb1ay`Z=K=wcx1zf_NFG=3FFu$iF%FF%X_(0$x)us;dk_xrEm5!$#-E6JmDE~Kj9P~w{)NI=8|m%g`vZrko5%d` zE}ji*=sig`!-=b7#*zNZ%z@19jphD`C(xgk>=;t-H;{(u#AYds{X@|qNA{B#z&jAY zevDIqEKHD6 z(8ox?L&b-Zzfooi?ij`v!(@ejvod>V^Ks+={cgzfsg@nDD_Y{I`=> zV54?glFGkgT$>emLd=+)3c%TjXJOgKWqchKXO%$cqiUatDz7Ioqv(CcBBH&-VS*irkDpW`?AI%1iu5Gbz!uoQX!T>7wq`rgZ7emGMyir&zaQ*j<%V4|Vbo`;^SViGl|Y`Q zE#~FjKSH8AQSCeU2xk(T%n61?dUcD;%qwjdHj;La9vUjQtz(1)4Ir2(@ke@#M%$uJ zx#6>jSm$m^1&EXUM)m&QPG1=m)ZLp0Cce=t)XdeNub8;HfASmjT)*N31wvQ}kjXm< zh8Y(z>TBZO=C!o}A`)EV+yTs23Xrp~yO7ZGCwu+Yd^IujstM$|8}KRgWCH5R&U_w* zsaX$we@~?46!BJ#^d%~3ZG*p_TxN3M>jUZQW3@O3d6DSZ+<29hI1nsG9y8P84!mcTZW)_oasp- z;X=2Q*Dpbb#*Yc)R^8EdqMzXt1?xPY;p?m(=Vt+n`CJzu+)}7uPdV0GSNocrD4vq+ z_*!{!quf`Bb@bIwkE_We-=f4jl6xk6mBr(>pSWx=#&9RAB6WS*_hR?p=5VlpqXc4e zQds9DHyhvuy0s6zefT)hjCrw^j_z|iY136vVtyGPUTOaBMIyaF+f72{Y-9X_VaZb| z(IqM4^SEG zrm@w_+>*5)Mb!B>jrx5vQ+C=)gRJGY7K^hiV&g}w)LhzMH0Q1x;7hkuMhMLeK8nem zZKT~#4`EaMmUH*TaxnSkl>Al7N76Bqp-=7?GtqlI47U{Qe>21nxpRkAQaefK#gabj zh3y)W!M8Q})g+o|E`}==vuf>WuiJ76?L@D)cefLLIU~rEE{(3WTF_0<2T}sE=cc>o z?zzVosuv@8Qa>hhP!PiK-BqwAFRpDM|Fg>d{yptmIk){P9hHZchuI#nLyeR_i0MsL zl7-Hln+;b6yo2JH|7`!E=R4g@CM(PXc)o%latBiB8XayaSX{P>he19dK%piE)Vqu( zj-Ri)r=8kIH;i^f>rpfQs55I0L>1MgI5q18HV<4=eAG2tz%?<4lA9nV&rw`*IfW2@ zTpQp@cke?CMS^hehVaj7S!BHz?WFGcP@Ni)uNoT*v@zTc*`8K*ao-h{h1=4+Gh)=e zG_(j-5S@xVNACMpONRAUnHSWGe(mRQ0J-XflQLJc5%())tYDpGlumZb zCig4r8q;?^)vaHp88MP^{*X*JJI?-HO`@pWrmf=QMwDa6V*v9x4z;+czX5xcMXkJB zpAtx`1lZn<sCg#7faY%H3s8YZ%^4+Wm1i^b444)?%uWQ{3Yf`SuxElb2qT-Ver9;``g0F>B_< z4nGa>#S9<(b3;2VoaXhX1wTa^t26$rxRM|$l80x{d|wo94gkQ@sTRk57^m$F%_7Sg z0bKEmr<@B?5qUXV0n+)3MPcDX*F6Up%?!V zN1oJ>_cwE9M*6gfy1(RMn`kYtAcjdW&#Bwu?&$;6DWlTp>b z>bqR4a;uMKTyW{}hyVjjC*NAHG3ib|1(ag7zmv4|sohXU)uKf)7tMAoo;R`${o?*) zf}oXI)hsSP9VUwvaRc1uDleV8t?Az!+KZ{Cuzlzx0D;GJz0NhWV5oO8<3aOeq>v%YD#9FP&;*&)73mWsv>^3U!37e&Q}Y5K~&2FvXlKV$+tt= z2D(*o?!Bgah6K+emADrD6cWo2- znisb?MN4Lq8WH})8;Z?ky(qKI!rE~june|o`S{JP6LpV;uXg*tB98?ATkz)V15+V< zz=&+&+j}r);WdQKo&AaOJ2o8{+^*B8E`l3f$kD|qMOS;y zd%QM|ZI!M0Z^F{z{_G*Zys-w|;vkp(=Wk^yG(&s7mpf~2^}qCvjPOU@Ld?$g-vCc> z!mo3JIgsi@@r$< zKSVy3U(lz)d);I&s*ONwLsEv)bZT%&F@C^VzFsG^aQWQlj0BR0=Z696Qp8DbleYjz zT#hN+pq_d^fywe*b3Qp!_U)!xj-5BQCM1D zO1=DMT6L-W)U*-nhus+#r?WtQZGJ3AUOAPgPUXZ0{MAc5CCI7ep^9o@?O9feH`*!) zcf*LxU9FrLV$S#%@3zqjla#8#*oAP}MG5T=B;(3-1))m^D0Rp9fE4R3?aOS*0MCyk zW3_;n-wiss*jL`D-&o`_E{lG;!-4rxe#bU5eD~+eibfA`K!$63Qji@}foj+$pE|Zs zOR@7t~=Y8*DNBLXe`^NcQ+%u+Lmnoc*I7ZFbWwi}JNsNKm=^VB8WMb5S5X>jj zhN-(SXYX_|`av5TP>R=T0yft87i4Z{_cP6{JsafS4UtiAx;LmTID`@O z8vX@t*pT3N&8p^l&Z#PXlHS0rV^%{%OYp_D*Q#r=n9I+l0e5LHN+oSxJ^4PH{i`qK z2|ycyY!-uDtE`vWvTIy2gS<|P``8ga_-!!y2d13gwyLu#err5G-!oQsd1o|0AuFJM|P>!{PRP<91p6- ztW|B#X>16NKgB7D_qGWVjZQ#(d)pim3xU2tEiYgS;2JJwsQPN)Niw<|C;9pt_l=v3 z7?_np%j4aYhx*#ec2Q^?ca1*&appqFDAZZGV0lfMl?IwT8M`opl8mdP4j-$PrE}jq z^nYf%%d9nHl(zO`-gF;82vvtyBkqy=E4g=f_{Dgu_4|s%4YBP`i?1n`$NHsD_IbB4)a!FAQzn6_s-P<-4j z-{68r?&ZZ)yT@ajS#C=YQgq~IJI(@5NBSjsQukr};HZPxTr&qmv`P&yGhxQ|Wk9*6 z7QYMn1q<3kfg@9ix(O=2O%-0r`AyEEe*K2m&iINN!8{&W%OTIN0Mx>31BCqw6@+gE zkCC4tXS0};x4aQz`8c1Tg8_jG?3QBcKDPgcHoHe4|(k4@Bc(%DMZO3Tgl!Ax*VDvb- zYc$+wyQ3`(7~er%bq3-VGEVMBC*~qwX=gsn6Y)E!2q*#U9-3-moy&sp!yt0|6e=ne zn$b6xN+eRsc=uFNKxcr&ftWRbGr7M}uW6n02y1OEtI`T|WUJO=fFpr?ME&6KVal&OsJy|?|I`y!xcisjkvW}UqS7J;e^sqr7qRW|T zOfF_`EO3BB=qya)-E0XezaT~}f$B{nP#CtUZ$n)p=(%g~jklp&nM~H1h)>`OK(G*9 zG}jNG$#``FT!Uw`nT3Cyis+z3_sS!T_N5e~zu5J)&b9!QsYiUTVQA$5B3eG2pgIex z{n^#y5oQGSe5;>*vI_;~(a>&BR&|tueSCxkBc4xHee1XiAnJ1yX}9WO&@f+1^o-X# zFl>Need+9Vs7T%_m}Y_)i_6{5&S}bz_0+>jn+6(NI?1yNEA70+hWi029Y<$?aBz{r zszpIzxBo$z^`nR(xoMy$pQ8TRmZymRw4NUe1_4>i)<8yCKmLxUhr(#1a%px{?WiDw z79j|S5jz2YZk>@B@}!%GeX(W9J&T*&9OA;9RJx!E9Qak-1W9+Gh=Er-qA>wfW6G}7 zy=lvk@fmea&NGT{k1TZx&2e8v+yR0RQA+-4ejTt~BO8W}UWcu=J5FwNB?66cpe?4{q(4BbObE8nytUIf(<^o*7vfkF%@e!{|iN_(E0j~dtT|~4g=ZQzRe!W zI!0Nqk?nlq#|tKwqxZVUWyJ%pvu#C_C($2@hdHvN70_NseE^L4-hQ!Q>0O80OWLhT zREZ1Imj?$}tUriR!<%mp+WCQ-bj=|QHbiw8{^JLU6TfrqvGQH^Wc_gpms`Go`rwYn z^T~APB54t1(?(~Sl7JXp$ZwKUGhc(}3055BnyXH!DH(r+3K>KtPnpvlH2wI2mVnXU zmH=d0knj-$^w`Q?lB?EG_j`kk-aIk%$!|K7v>*GV$!xpNQT6kx4G`zq1)Z{QSCh?2*~+M> zVh!;>Wh{xQ31msAHS}Asrr@b{^ZKBTrG8ki{@cbXh#)7dtYIK-CS}~dk8_yJ=pn+5@f}WPS6zI{ zM|CIjqdR4g8b2xWu;UYcm0c@SbX@5tD#mTd{&LIJtxL$uwt?K7qEWf{F ztUR`07_VcLY?o<$)vgsEEvE`EbK~HwW62qp_oH){t@_)#H?!H~L>vE;W$RO@v4 zV2rW)4(N~`wxs{-=)_+}&i{my@UijG;T`^rPRM+CNLMM;0P_bdH$LjvAmjR5cJCKm z#}1OPh5Y9^hN*FrM8IK3{r7yNHvHId=mnd_RtGuR!L$r`;sGm1!=b#Lx7Po%@_Qcf z`G3%GC&BNMfI8~f{*pWxM)*h^Yq+0WPo$_lBMk(z1Baz}?17!)uB#s(j@kCYOJ>F_NgQ{y{*%d5NPL#?8rrI`bLtV+U&kzW>J&2AERo^BIf( zGKT2*bL#h8$hbM+b+Gi0cZyXlSV*gF2G=(k*e zZp*RJm;9(QiF6%M{2!2fF?IC7(Qq@WzI{$=I23;-kikMqm_HNyX2jZg>GEq}NR@LMPUYmk2j zwf}37|4&ho4yLe*-ACZ+sZJgGw}WC+hD!15=#djh4X2-J!_2B|`^z3W31FV_^R*uF z_Y%Z|lVZ=0on8e|!QZqOWA*-BE=X+Jgn8<`GgdeZg9kAL>16z%IKp!j<=R2}lD}8h z3i&^;v1RJriMz#*OUAY24%QUP zq@pb4jtJbLpgdO&D*XApOJPg^7&YhCAycu%3ievv>hwpSVq##HcGdlra^QNIwqsSf zNXY)a?DhT-C|%-Y@x4kmGpbwQ((fZiEd(Wx2|VO-h&`arEM4@06)^3t8xFm{bHpCk zMd7mVu#gBLCpt3o3&wl7C-lsR%>&1BlF~UCNJ#&_c)5h#7faHVbAb!+t_#Nfy&G`D zF*x-pAxA+|JG9)5-6y~WV8)o79UBIH6)HZe!{cRAWPvRYtv~ew{`h6(nUDB?IrEpl zV{9K~eVWx^hK-`@R_(4H4ZN$Oo3ieyjGD~REg#7nuMSu~c&`g=mOPr%6AC!InSU+h zG_(GcZsQ2t&teM-YIPrD^`0kMcq0T9ehX4wwymW-0|6l-r@$LUgI#Gtzw?!vN?fK{5I6FW<#&0Ps7mk!3O`nmcx20PjZzIFmy7n0DA|0 z+$uj?EGV9#wr)x5%f!I9pGrjaP-0)!{^A0>)P~cPB3%(p$Zs7Ucm<}BxQ z1c?#Sy<<~M>v3n^)_uI{hBl4N*z5JY-A4i@nB6m^R|B=rq@ceVs`+*()_i7?mim^fpw8m`4k>9kQ;YEwx<9B zE708+jcSa@w`+lEPHO1g~Kj{wtU3y9-~cHtc1} z{k(^lQ12B5rnDO~pMkd@Rg603LWy+Y&(sDobG>8*CkABA1!8xd<&e$2PoDPSLey0C z;&vxEs80nPxEx#)gU)#4(lL{f`e!7q?lHZp-%Nw#BQwV^-ws!mQRu96|E55AoXv9e z4D+=GzoxryJ}#yf@Up*EFVb?AhzrG=e*T8(Vm0H7(^EAN+lu~v$-?%WWmJaWGJ-^mWH&$ER`oJ{8gbHOD?}|P*oh&Y@h?yg zI=@#3gMSrY7#sGam99QvB)NONA1V!Xv_^id9p{48mFi!CnNE_5csPNHIF;D}UAcP8`I6+RD8>LMftUQElC%=x=xxlsFD?fCH^~B!j z$1QOxJM9d0#iM+?vf)BL8V`xpAJThA^YY!YWXDJX<`4Xf=g`B*>$LHVNA5|zhdN!G z;YWRmOVPUhQUTnkO`X5eS;p!G!EyRRM7D>LrS!yC-w~1!Lil5W6^{|?`qAC(0v%D+si4?O-6BM{T=v2H*#u2gAKf)#H}$~q2$stk;p z>yeS)uu_~#i-IwNeKD9BDe6;^2M{x}cV}~0y$VG2u8;`b1ukDtl~2Qn$A;AX?s}e1 z!zeAnH#t2zj9uikQksnO7Z;beG`(6)S`~;_QO;#0RkHnqw#7C&n;2jCr0;hJO`5K@ zNl*R!_P)FwUqKhYxB~l{nF_4SM(6@~khd?PJr0|}S#^3Gesx*fAU9bD5!4QI-KroC zB+R(ti}bKO$R;<2c)H&RlR4ka%}Vhriat19feWF`^%J_H8hqk2WyYO)UEi>8!YM{H zg-ZJGR~Q8tRlMZz)=Rl3(B1AQ&zyY93F23Axi_#qp}2oK`-MUGf>b08@z`Zro8zR z=X(@6gQESq&Y#Zxh>>fihBZI+^AeD|7PH@zm*uq5Qla~)s!8!Ac@xQ65PRt$^Prla z+k$w?^FqDny6e@u_Qa={$`ReHT|Ry`>=)~bILHjf%DPh#p1ERJ*6nI$X!ng8Y{I=G zX%wbsbIGx+S8VqE%3hg)h*)2>cg70xZAau2-^n!ihPWphGk!))-uUL9G)H4{`y8hj zwd;HTms|xe{Z9l@qN6iHUR+*~me&(IX*%f5(bR@|aa%}l$5zCYlQR)TANJ)liW5pm zLhKZj*{&g{`>_=dr+*Rp8xZEUb6%}4<|}+U#8ePE1^u9%=M4e=xO)Q?bOw48QDl;F zKu5oRz|}f)M*&i3SHw#GdFUaH&BkVQe%|%|CNwCl2?fIm(`4Evz z92m-X$xGG489Xswp}W)aTy-d;NfU560)yXu8^tr2{6f4$M)0rw&4ii+RUsc9?C{vI z`8}80^`X@pjtnL~^45@=3Y;v3%B4OVa%2~+v_^WN^|B*(xKQM-QZVbe7z`6aazGoJB-8?Cf^6V1L^Wk5&C5*s?=={v^wq>F__Oz^^n0`$=pI7Fp7hhQ*gUjW$%PtC_*4zHr7BMwQkdHz zal#t(w!(Z3Q?zaC{VerEi$`Kx@LV$HZPj<*W?w$RJ^q@doCt~8_w+JvNAR9ouV@(t zA`WbP65_VQOHD>~ERLjHzbeT@*7LqU!5ZxOtYz|Idh=Wovyxb@xu&0NjvhPD)DCQ= zZKcSoXHH$!17kkD;SJ-~lwi-(M(K-9^4Lz@bET6{LW*lm{r}@LOgzaP2h|G4rK2dc3bSS}~d8-0IXy=B-{qvKk_z+i> zwHTjw5HuEc$aIw?8Vsu&sLIgV$NqfU2N1!?li>0+Yj zRdC}}j>?NuRn;}Daj+9Z`uW~noHQ(hy&j1C70J)}ukKc@?3Ua5AZ3teR(2}TFrLm^ zTBFaE_7;XXO=5lQeSS7at3hwx(@(Zh?uc#tLXk$1fjJ}RB8b)4d{(0&YpNw#Iy~_9 zvzn{mHz%X{B%<|sr#vva)dlXm0?CINVgqV#GyF!Z%(o@x69H$Bio+ad=F)U|E?c^L znE9RlOQ;{>!&z$+UrW1iNM`C z$_~E9wlp9&)1b`#Ye_c+hUi)v@d^-)>OsZMb4};CWh-PiR8L-lX}hEI7I~9F3+%;n zNnJ~NjTTE){crWV2vg>7f3oSp!zIvB-Tws*2PNKybQ5KrE#)HyiV;P5dV{bNv_-K& zZKb)pSQSI>zQB^{5c;9$u~ni#xULOnrFU||6gw03yxpzF@~WRRaN@bwzJ0Q}PLo=3j<~E-)f!wKTyPe|sAb3u zUwy)I_u=B1n5&`EY9WRC_}te|mplbl#K41*gqUt0!JWOb;lfc}?@0J&QkeFHy7y$0 z3;)?JtU3YD20tr;g>hJx@bBBG0~^rCoPC&nWa;oyDi<{y@MV_6);;+QQ?eyYB`m-?jTM3&#mh5Ky$5=rl>-8)61K_$al zP^fzk5o8t}Z3llZl5J2*Tk6q?zyZ6GP<*SLA6w->u-0hmlFV$qI_s&KSoEUCKsyrp zAOz~Ey0j{abhz9(poCWSg^gg2Oz5EV^UWl0%#T6(6&G{$Su#cv5GRYi=fI{kHi3$< z8st#UL1P+97xSLEjg#i~bmt~^^V7Yq4Y+TqJAY`WDC^_ZY@cmghcCTLPdMkin97zp zVPKB>pJ%=<_R%5L&!`eSOD4r;E6kGI^SrTF(EpSw#xO)wHDuuQEr`}%t9vv zP)U91Wlt9)1kLrqcbwK6gK+Hj>Tj{`z^`(8{ZHck`V)%X*#|e$axh*Jmxn)!X5Fxy z4_SNZZ1+IUYq8W~`H75Iek&)kIxSt*Mf`OXEle$VsVRcIDM+NhXs>%Mq>caWds>q! z=dz?vblpfuJ4C2&Nm@eFuYyb#R~exPTbk~T2-_nIqVC~Qe*4&F-us<_H#CJ z?O$+`x~PZ%EiKDLy7nO~&`UDWc(_~RxweT$4GJWW+(AR>TcGNnLUi^&0gpBqX62{@m#vYUP$ffjG zakxw&8bPy3Z#ZWlnGruUuf;d}v-hgYNtwhrSx^8T`hka1aKQ;y=Q5bw<5KEkZDrUB zM}1krs@@vidyxR(zr=_y-&mO#-9EJwQc{XF&D$3;h?~67;n)m-_8p4CA`DN08Qq zQIl}!mWoS@j(mKpZ8NWtwgj?mzO8g>snE+gP^8Oc0X7eeT(ga=$hqpG<0Pisql z03*5U9&Njm>b(5sY=jih>W%fD^Ubqfs%)DR7TZgL#35R4hF>n4F!B1?&1(}82buCT zkTr^QnHwU!3M-6yU}R*yk0R>SP^4p9tg&C5hh4daV@9W^wD+i`W=s7w7Vuk1>Y;uy zh4IeB3L~jkrAqH%81 z$T0agJkHG5Td2zcnIj?HnYh7ogTKZxO22e!CR5xWxJ+Z-4o|)Bo>qFBusck&vUzsB zS)-8FD%oD6YLdBWqup@n(f%Bwjl~Hale`?MV@QZmA{eBBfc;=%$WjQtzsn$bVbNh7 zj%?tu@vpW0+gqDu-J|*w`4*_}#m{|-ljCsr6rS}WRN`Jlsb^_^ttSXFtXI2c2o1+Q z%3uebZcCsSBr*5vyR!4hD*2}5grXw6a@|1e5{OX+`}kJI@$U;+waUcu5WdF%09NyWfD`hr*iVY zXMP*QEM@(&;A>K&(SGF*F^d`V&?ffcsa@zw^F7bu;7M8-%(={P$L9Wrg->)w>LWc$ zzI>6U89hEmVGyT%jG;8v#e^3<6FOPK9M*)j4W8iQ%+mfGMLVA9rPqdjC>Y}dyDfgF zQn1jnHO=Nc&LaOqscUCh>%;$M7&3AN3Iz$IK&)S)b{~5_{-mh)lQz9am@d??(ZJJv zC&0!;BWu)D#Y68cuXHTx9PaL8UiI$4)Xtvil562JqrT-(}3*OE^>_$;X zdfzC}xk95~ zxc>Pzq-WU(3@>4%pJA8VK&Z+P+&2}N+=2GeY#0t`bC_v+K5p@4;ZN`1%xgdrWzw4+ zY2$RdTxR!Lk=NCx5_TW!;l6DBKrBb-8OZL$V03qE@7r`+hCetvndL*6d_dRKNibaU z!fO-sXFuf&ZhE1uGv7;dtqT9ecZ(P+1-%*dYlX1~6ce{)z=|~=aqV^--w5MuIF*d8 zT&ij95goM-)^4J_A)^6Ey$$Wy=o#l7n;Q#nk_?L54dq0?8w*8FdNdEd)bem<4(9bV z$iGQnGLYwJsPFc4wm5?Ovqm#<(oxm2v1cjVa=i{2QBHUlRD(|4J%4WJVt@b|7TE+9 z&YZq#P)dxi08ixy(i3J}b46mOTa(! zDgH;T?OQw!^Og+n@_OU>1r=u%b>C-q^qe+Ib2LO2f1{DX=W05!Kdzkdg018seS5Jm zxL|B|#fT)}6nvnOZH8*^0o~_boi|l_wxEY$wuSkf-T9-9Cwpmv~dx^1y>)?w_ z@247;GS~8%QeWsVg)Pnk#JFvSy9vryup+?{51Wtld|(eQgb)bP1>pKIU&8Gs2& zim%L&t1}8#BI zKiphY0yWsC3U8n^%!JOi@yOwBRh`>6t7*c2f9(On&3Sh2>*Nzl zIt?vz8fLA`hBE98n(viiqg*Xl@&KbF2SH*bd!4L*LC`bzF2*xTb5f@QB{|CCl*tiEjLy^GoU6c<;6pK*70!1e zDnEcniXl6_(XqQBVCB1V;RZSC%mVXX)1nU-(5P`~Za=H%LnBoT`R*!^xCiXqN%7`g zsoqvbLJ1gnvfzMitNP@Z)tk#uS86Z9l-eRVamVw1L_&|dff+YPp4!sf|LvG9 z?Dm-xX(L;k8@eohXN`AMErC`Z##d8Hg_yMqlWl{q4{yM*mCr62Vjy4?Fu;4L^t(QB zl@!vTCL1Rd{+XqbSrBrl@2zmKlo1Yh0Xz_KBl=RhE-5#1*PrQsw+;2`_l}C}DxS$+ zsT|26_>5XMUff>Vf!{QwH+)1nz-c?*1>)TP|kte4N;I)3o2w-X0}ceh_wC}FLwevtY6BGTuuiCK7<@6iz(PW0$zH+)##3{g%ZBeiv0tIJwY5hqKTGE+1|Oq7|72#YmrA+U86aD zEtRI7DqZBP=04FPA*ZYej+KdWrZc`SauSikIy(cAjE07%B3)WJr#0CT%iF0D%+|bg zVew`9wrJF*MKP%687p3MZ2|SzN(u1VFkfxM$d4g+OHOw~)O7I2tE%6lUYziLpz%#| zptZ6@-FSB%q`-Y)LIGvi9w(vKwjEG)nNr}v>6CThF zzc+e1#bPdarNwa&aW3Jx5?R4CugOf1_en5&*RMApMZUWaz6~lSUT-cORT2Bj1=BHX zXmt6|W04Po&?$b+=SCy6tb$htW3d_RsNPWGmHnZICx>GJM*uhPd|~Hhz2U$~!SUvI z^JnP`voRY5-%%-@;mj15u2dUVmkY+(L>OF@7kn;*FSadZ$2d=0%jnyM8G`7F1UvO4 zzga@xr@}R*r^=M3i&h@ro-Mo7e;VeV-LFMpe9^yXNa2>GW3;3XQQu=Ugr|x%H858$ ziLxSusyeE<@0+qiaTeemjkbL2W&R!A_H+!~sQ%VQIc@#jc$$^D;K>)mkq(-<50UA+ z|Gh!63w#ancY^}O@FQ@5ey4}L>e=(i+LFZBB3~{r!E7z9N>C|Bd)BJ-Nhtl`yZG>p zLEoww&#$?>I?vSM;ptbdg+31*<#lv6loH63IT^bEjuAAOp{1b`>Pjt{E}*xvleW2u z77_Y6`-#@haBGk8O5t)(`Ho7NlF8FHjCJ$H?FBDrukQESWwR%4Kxy_AA%?P)ih!d}O!iQrK^Jm;_X z&0G6^Y#+5|ZoM*LLqp51<44_v?Wxp74q{_`l@Zf-P~o9MYWAD-Hjii6ZUp zpT_0uwT^wR_}EwCvNy<{KC?NU4Z8Mpx8%kEnQ~y2>lUfq4yeS$G~*wp7?`-gbT{(y zJB`RYp>53#t{OF~*zu<4JF^%G!Rnu1uYvqUYHk`#C1b*FDU;q?zYyE!(cD3%y@VT= zE>A=xtDBuA8ZZVgqeE0%UlV`GnC0MLVv75(AX7o8%4z$-OFF#^@4@x8kjTuuQX8hm zYckz>XgzCI01MQ=bFbpt#zALFZy)uBmUy>%Y2k=|@%OI{dS*^jTR)2ltB{;|Dp{JR z-{ZisQv}Wh(y2~xi-ki!-f0BoHg6FVC08hOIys}$Y?lEShhm%L?910t+<2BVUAj?) z%|tMJWe$Csaot&bKN-z@PD-s6>HWZ$ucjb0*sLYI8q4gp^Qh?bBnM2Dv+q|=^IR|_ zJEx#MzPXAZ?Z`LE@8f20KJ1C%22|LwGQ1qktJhZST!zqrBMDEchsXo2{#hLQW+DKr zY@ZmncjTj3_m89>6VO^GiTxh^s5N=3*_FvRm*jgAm3wYD=PHPa@swnwEXGTDy(!2Q znW{mUuC&Sid{?KF${_LtdV~hC%OYdgL9_L9~3N9psl31IfO#Le~)4e zcC6G$$Gp)IPhM_+i#jcl<RepaATut@s+?A+67&pC37Jc;x?O&P1T%Y3hvaWhC$u$+-*Qon zj4eqg7Slr8z^pyPJvl{{-|~m)kXIA>2j8B4R_gqnXZmp>UctR(r6^rmvKKK|oR2Zg zQH5K@XFfzi;Z2T(iCx9qXKmN)rM@O3R=ltZ!MmCU@C%DT3((5Twgy{KwbZP!sUK;R z9eIs@UB;-Gc6{6ys5!+AounMk!xSLJ29w#lS!z0WlU}j* zEVQWd;qK`(C*EKpHiPt-Cafx#lyBTEgjVq-5c@WV%&ijWVt!WZq%X3_4QsQ@PPhzP z6B}K1Gg@QmLfw$_UfatJE>kIdG|zlWw=~ZuR?-bJBAUCzG>z6fUHAlHqLaKsR>5uU zh_*X5Lvj^hzl(UlFG%HMLWrC=#j4N9%}bPsQl2G2iY}%gD(>&8ZMW-M8eb+A4%v_$mjr{sx9}repG#XB?z>zf=FfA&Xw4{a-Wd3s7bc9Cy@7%&63+t4EJ&@>Jdv|^8>N}()&Evvtyb0#}}yF zDS*n|UtT#UhNE7Hk#d~1{x-d{jIecm{dD9vUoKVNaQhDwHLpS=y^WZaSCwsI>BjTC z9@}5tXH0u6uN!|dc|a`-IiZaVGLsIsMsVO7ozP-xQ^|J|(OS4VSDS_j9U%4h_`Kj* zl73CH*AV^KimpQcYUguPkHqXG?y9~S{bn-ljix<&*-)PyeQYbTw@U}1p}04%USTe! z*E52L_dYOWm;+XmE+U<7ZMvE z70|wQmH{Se%i#`heVk5RI_1-uX`20MS<@HBZ%fC^p<`to?!#VswFcwF=L6m;N>^Jp znzgnpl}Opce-UfVn+*9x-^B?D@#Dk9HGJktt4No|sxOu7OO~#vOuxTLZ%S}aYH!45 zrS{9gzeqZsZ}Cx?lt~P`RNv%bll~#g)=9-u*4b!7Y0)W35UA_WPi}fBr2JhmywSmK z!{E$AMQIo2--JRn5Uu=o<>ButP@H{W5uT_kKfm1g=R6Gum_aW-mjD^BxV-(9Hq@$b z>9)A?orP4dLPLZ0U{nf5MUM{s7NW@>C1(}5NI2v3h#h3rmaf)*LGSz-nBG@Smsv>_ zv6vRw=@MuaY7x)hiNGLG@DtsO={DhwYm1zt3dTAI5d>7&FcEB~7I9R(r>CI>S$KSs zVf!Jpk)OOM_P>=vXP30YIj53^H^?0N2UYi^u7s>93)Xl)+8^}o-NbexH4SG+YD4gB z^o`aqrzWnnBoO}9I|N0i3Ktu*t) z2RG)Ec7ImSWv`TeX_5ZFmv zd<}{^OHpyOZk3{kG^GYsKF|5-QgQuRjubn#7&`D{_SuP}mBI9bV$k~Z&N@+^JowI0 z2z_^>?&{(CTtMZq^JO$9mOMv`eI*AwU$78}1!t>I< zn+qv!^k(!&tAYP30*e0?!T*YYLE?hV&{Y24QNyR5bw4Jg+P+LyTzT>Iz1Zp~xWnokneK&LFqyKpDHGS#La_SME z)hoUWftS1~l*ENOs6vvPF=v`v*?dd^@(R@A7gpG&T;zZ!G^qL3e?7@|@KZ1I;$x*o zy9G1a6#s|0_l#K(sfPgZ2;{7 z4NWWCTl?DG7aeHYR_*$ao-;F!1uvdP=WuqN2Qd2fa7hHr%7F^`q`f)r-r=|D3jmQ* z@`23y*0ZmV5AWMBJgI!JVLyRD88OsNTJE)d%UXTmteCJlD(G!Y;H8v+HRJ!ZDr$cC zi?J8PE1JL%$>W6<7T6kPB)$YtHK=Zw(c9l4aNGYtj`VdNp&mdZ4weDj$sdc-GOEwW zF0L}mBozh1SNKDOM3lLbBv7FubL_Do^nEB8RcR@d&lc z>9Jb!G<^mUb$a8=X@-@0?}v0b)dw>);&zvS)$8^0J(El+H5UwS#Q z-Uz6Ed0&w7kgqbAgU{}1_RzX-!|)%MY|i^xG7mA9gU^|v-r_mv;;Hxnan}Rlo(ge? z4fa7_e2WwKkL4RQbF3!qvpgh=xE=`|F33o1DAO^>99%f-MsZp3Hz-hFOOS)fg-?9+ z-C+`4r0pP%3G%xWhoKFVMR2^tLg`Go{4m?rJ(`vN);h)f1P&|p*&{e zG%eWc6vq!iJGzZDP9yen?eVwYOsyA_;csY8+qzZV>HT&!fdeUg1@pQ92z!1pF{A=} zp0wO{c*EPTESOqiW93*xvzSi&#A?wGSk(bbW|TB}Lk@rs1(sWdWJ1?LuNf$JK>Lw+ zj~pZZmz6(RClrbdfo@cul9|b{b_iIXqNC}YJ%R6a*Z#JQs~h=&e5svbU>sR@fI%GM zdPx`)659u!gGVyb;Z(HxyTnJ{9af_^Tz;`HXO^aR{XqY^_ZQbA;lsrvsbfhbP5R;> zqZa@8i+ts&!^NFincO1?jYz2*?N?vi+=>5(1LA#x!2x*Q909bIxG*> z#qYm_GFJYwVbDSitg9WadThg3!J2QAmjR*Hf4H3V9v2NRIOf_9wAH#|HsfbU^PlK0 zV*;uFuPG9v&8xpw<1fV~fdniU_u@PnMP>a14wWs*#c_A?s1nhPy5d*cyBQs@2m?Ri z7)R3TyKP#8c34dQ&amS0Cyw(&pNE7H-+hRuIcbeB=az+cLX##O@M*qwHMyJ{#yGw{Rh)k&^WW}jT+ zIOyhhx-9cy5%wf~i|b(b5x}(RL9RW~Y}=rr^VVNXsDact25}4tShE9ry}LMZfZ5GB zTCW2v%*~r7X~!n@AHP>8;G2yJ^Ti*7dUBt8v$RLU;kB{Zt;kVL?nioSdkNs@g*0}2=T+L>)8RfOPiImwN*3-S!1QxoiyQJJ~3&QgBPkQ8*tZ~NVAL>|SR!b&I*>mDO?#C*-a}Bh%TAdz{ai7Z694lkjEV*-f4N zf!!;O&X>~F_(sjFM|52~Zs$#!uXGI(wo0004n$QE-CDSVZQTtfU@$8`B}oH9LQHF5J8ezhA-3wGFoyj)z?$!`MV!S9wY zzqm?J{!>vefw196g=zCms|fT)On7J5NAC|{#_V`&u2A}(2Lu<~JMVZ#8ae|L@wN(4 zD!$GjPH{<5UZug4zuXUDq|a>v~^n-Vm5;Yb`4`PcwajeKIoXRyC}IbeULD;59&CE z9=7Lug%L?U+S;F7C&|$2ju?*qxdGsy74JaI4Mw7>F1g{xM*(oYcuU` zK7EKM-*KF>AQwCPCr6>qQ|{`_+#Z~gr$4sxRCe%}59fdFSW=UFo!5`7&mNYvBu7nB zy*p3It{}SRaLsOI`UEBO#L9F>Q8ZrI^_Nd8LJE)UBw#pf8Q20*R%Z`IYx;oOkQ)i) zEWFS1)=uoP|Ft*YTxD7M5oewElRLYjMR)4-7`+U6ZZjqbxEWxM{SYnzqP6<8k;btClfGh9=p^yJb>>xoOiHhgbcSJ8$r%7CRZf zJ{FJQi(?&s?j>}Rk`>Ut7-cLe$mF~?+Tni2qf&I^FwPZR>uh&{$;P+WsSU`EyLL^@C(2X`YqiQ zBYkqvn971XhFRxUlPY7`CCK!Co=;<}$EKvc+%D0G;I@kS@prE;SJ?3MgS9Sxaa_|c z3ro%UkAIl<~D>x}3Hp#C7d4INtuYUYNPSNh<{b^d9gZ%3Ry>!L{jsatbC*aXAv zEyNwYS@uTlW4O|nOskFbIu$-EaLbH!l^VYG?!9{37&*<+l-`sr)XEg48FfzpOIxw+4_y0URKavaO}ET_h#3jl%YF zL&cMLiVG!wng=`L}rN^#Vm$ziYeHG~>EKGUaq#+RneQzNUX%X`=M zLUDVdXMx+N)oN)qySxHh+crVkUFruP>aIHaa4?lysb30x@n@Cc24--}XLqYtb@FTb zo-Va@LWABz$9CYpss4x6x4`y*oqmsvVn1M_ats{3W23wcJ?u%ii0gc|$2G`qx&PKE z;+px`ZtU;7-HQZ}Z;i!zP%2(MSP6W2;2C7FXzi_J>TlFXBl!1PdONuQUl|7Kg0@?C zZeo3UO57jUifo|lG!MbJ#)_g#de`a+VWr4j`C)f#;07B2yu;wiKGReK+^(CieRaTb z5}?1}LGo!sO4#Ch>)v$tUG3SMHQV`W@zBEw`iQqYI9Mn(Y1Mr{H=r)km`EOwR18$7 z%MqIINZOa3oyPe3m*&A5A;W{xvaU|4sitLVKltJ!MQ`B<0 zL1*GDyN>4R5xbjGwFIdE@;W!0ZH8uklafBpy1bcO&2h)t8Af1$4g7N!;K-fhKKpL7qiFFJNES0@o3 zjp~i40K)>#{OzuWM5aST7PuX14~b}w&Dv;(;%jOAR~nV7L4YH zPBpav*eyL(^>~CQ$^Svae|mO=i*7TO zQpEKyANT( zxOhYTK$syW=E)(X#|6C&HD3`juPFkc-5>P&ZNH@=eXi4t_c+~LK7~5Eqwtrf7X)V#YN=%0JnSKV5lH!z_C>iab{_sV!XlE4>if z8M9w=ew}HrI#{YmCC+1_ zzRZj*R-m~xD@6CZx4Yfla|PC9Cf(Ffrvx@t-(BBJxOu$4t|yU^(hp}QGxhXNjtcv( z^H8C2bD{?dweoaqiSaXI9|;yxNzG3?IT;LtUh|2~j_I@-eJK?aA3<4+4nAUG4x_>j z{>%(&zbkddrkYLH0rAMvyPWf-FZ1V7KDw+6^H~V;Fec067HR7K)grtkE?7%qV~kBg z5({y?V=(iMD9ayW3as5`^rhxf?XY(-{FPzp(;gftfc>=sA#oj3Ca` zbyqAdj7@{?3sUZq)Q%oY0P2+~o!235c)<6o)bhKoN@npoaol0=xslM^|17TLQE6mt z>g_A3u@}8+X>N-k*%p_%V2TM`sq}c#Kn33j&oPa_ijYZ(GZ;fLTF%{slhJkX_39mg zc(Qd>cf8=powxRt7IjMGB?LWRb)#NU%O{!Jiq;1$&ZMG9Dn;tNCH$y6(2FNZqNtrN z7uHOClNp@f3M)0BSsa6Q-6;LFgAu-u5xyJjuUn9}KF2)7ht1ryzKj&Mq-@m0M^op* z0|28cb$acEX^c$82YP4DfgZsoE_*kC?nea;!t|L4t02UuRmkC=W;J+|8sJjT-tETt zTUBAF_SJl)p6P9v)+j|tr(!w*h+tn{j@ilG@vFvC<(+>F40`THJ9>Ru)n}f>u{8r# zwVx4lj0`qGW^KYAsB~_70@DB|b-IX|F zc)8$Lu%y63zbk|TyJ*$|d6ZG!3_I~_b&*6Dmvo?@wk1VP|4z9!M_a=(DDU0gc7jaL zHap^u@T7LxabDOsjcu_j_6@B|w4Y%Kk=vyTP{7I3`(}$heoLxHqc+e7{;$Thq@!E@ z*bWQYMHWLm5VJ<32_}Yi+P&Pow6B0ZJ+!JcM9bmo+1oQ21tfp`14NzgAzK%WbSLlK zYw~SO#1cn3dRT)!x1nZLGz*|HE z(EG-DMz$FlwP?3~*VC;Q>XXipcX|g$5oFbVj*7QOk46h}6EIR}?cb`9P`X%Gx(e7N zqfGs%6aiH_>3>u`lR;MPV@h2(N^6iA&Vm^P;e(d6z)XAF-{r~w*+!d@f9HBtLXI6` z3$>(vw94v{joa)xLHg3dsL+0Cbe;3jQpaWfT^hzeA%nMky+MDHN*-LU=iiP{oL^Dq z4=J^FFPFY$gjr%H*!~O+Cw06sk~}f4$OQyYu_V$OzPM-@@|6OT;klP0ap$n&y0wpP zmc*@EA8wk(hwQ^LUQqHe z01E)p95=g_9@MaQoQyV|@~v4`J-g^_39YjY4BCD0XVr`BSAb46`Ga2F6fOJZ=&w!R zGmSlqp7W9LGCk-!e*^$Ux!t5;7iwONrquP);=72j%!L{B3ZB{&%!9Jd-lzd$=+;vd zv)oRLj5yyQ8wN0?u}zZjTMnVUURT`KwPW~2ZQKdin54qSg7+1jhTl|TtzvSFzy}X4 z^{K>sH65XdR%SB}^=IZbbja;I3t-nZFd$x9EQvpgAuszUoMy0@sv`qmh_9t*2{_pQ zmzhH=kA_b5!tTlICHM<_oidkjI8PHZdt98r1Xcbmy5n&m>DUwWDefE(x5hmtuR9D1 z`6=#v`~F)Yf&+#*z$Te5!iuTK;Fkq@>B{wIPQciz?%@5boeh6X&zafi3;OotkochU zBrSkQ>P>gAxnnXQ_}d}e;`acfU>srNzX*y+;qR_Gk~4VMldwvFaiZTyYW-2UL|Lw*MDJlDEetQlAO{9as3cgtjA0*k`o; zQSv(Pm&rUh>A7AVjo58bO}G2TbxK0a{SHuA0%ab=3)x=K?zy%NpY*ODUVpzA2pXB17~95sGe-jE3xY*Q8AxfgKcrm7TC-D-XehN z?_3+4a%WGnVMquNtvY2b0f4;$#hYffBuq%fu^9R2R0|}?u8191LB!rSjv5jfCv!xI zmN_`wF@kT2iyWbkHaH9u5u2#P-nN~ZooX$Par5vv&+wF^<0@w3S;}a~ItjOU0H5ra zN4^qok9>NM1f;jXnU#2=j}lW&?}ffB9n2w#nzU|PRKwuy0LgF+A+@^nc3R0qkoZ%n#Z3OfKLBG z7I}`iC%r%~vEc@!`4D!f8(IH~-tm~HQ}rTe7g0wYlOnbZPaPW;@hR=?Hyo5enYC9rjAmZ{M z7_o{jcJ}YbaK#Me2l;8-NL>oxuBxJm`VV90Q%IOY+>uwe%uTT{#QKi6 zeXGm8=?Uo9G}I!l`k2zh>M?h$llQjk&lZv=R!Ctqi7+fj-by%VbxSjvu8Rj2_P9vD zOTli<_Oq!lK%@_pH#z8ic|gb&?SY{J83Cl+VF{nQ*F{B++vgDczqPxLfv2Zfy5a%J zJ7kGx#P9`F!h?E->viAJMJqYIPTM7R^_Zmr^RUfpy(s7Csoh69=EGY zW{gQqv55IUW(ypgI_nk4_IB(}VsjO~Dni^a$bHrw_y!XA;IXf|OlSgTL%d=KKAugu z@nb`9!0|3H@R3%9*9ESlK$Cjhfe&T(*^(IXB%qK&06wBOo2-TG!ASSVIN(EYK4kFa zgc~~mcnh6yGi+r=H&)@nSf2k|54=Vk^S1vPNwHdb4Q1%+5=x>b2YCY6L)q>LT3G^Q zSnt6h0Bto!%EwP?TVDnqzjL5_6`)?%+X#52iuJ)BfWX1ceGlO4@Y<**;H7~@fUmFZ zmxutrEg=J^nVMje=rrKB==mKY@O1zYBLrXve=Qo|(b$gl|EG_Vnsv7rjQ(mKp>VQU zyUXmsz9@|q`7gowM0p2_=nFJg6i<@EsJKHL{@-&v)Wfd!S4${FM0!P?KX*?TU}J=w z_(QrF=Kp^(=zoLQ6Y=0;>zwm`EcFez&6j-TK4*_%U;oAw{r5)cLAdJK9{Kmb04UmH z3_f!J%G0G>M(C9oLtpch|8J1GU|`(dudSsyK?sxZz^Gg6w_o4+0R!r{VeZAL9LqOF zDe&`uz&L~a-`e`(t!{PnShIhDvR41&f1;~zrm$UP_z%s;1qk_>&v5IbUmud;VS(_IL%#u|wNmCTHpujK$n3pHe)cK6#H? z{U1u~Va6`}W=8=docZbuYqno~PsjW^EMe|o{0d_B8y$&4m-Uvebf7MyTD}#X`-!3J zcA2g{uW?0&^<ypXU5OF9Zxie8k2iw_oK=Zze59~iqBV-Pfq zI38%|R?S)WS3cs86H@tq%hlH$m=iejd9?uXD|P~a6;t9d;N^6$m&++lE_b!M~K?^S@}hw`BZHkp1Uq#?Bvd zjv1}})v8MkMs|}~sF}$U>#-CAKsftDLtU|TE4uB6$AvMVz4CK)0J-@M5C#6CVvUWq z*X6R`?=Y0yG-iWuwt-H{513-(&1nh%Vf$qs7P12Bwq!NN@fdkA6Khijseg z@$GNV1Yq>*e$Br$e&hQZ2|n>Ri;P|9v~Pxv(CrWYY|j2tTyW5D0L1y76xK?|M=6Z| zH6s!YG7gi-N-q6x+E!4*c|yVXA8AZU0D^kCjre??Qi}Qk^5Ug)^$b6iap;?GH!74p0(co6!;gXmGbAKQ4Q%1Ey692pP&8>qXs6p*@&2OZL zx9$S|K+U0^{&My|idE+;Eb)&Wb8dJ%mHK`3|C3)jo;&*6K`ZmqR+cnpMSed21TZ%c5Xr}5k^C5wTdJasIGW|C zUFdz5Cu$Uw`z#3~b431o1m|&eH;&9R?AB1CDs!+kf81!F?JY{Osiiib4^u^uTW+K8 zu`-q=T`_pEOoM`NE1=HWJ8pH?BxquuNr{}&bBnQk8{nR^Qt)AQ6JEP#&-MK@IM>EPDo(5JoR#3V`ue=x2Fla1&7{mfzXNI3@7d*=F_STW+SojV zlhn!~Eo^@l>@U+oUthTvz*6)aD5Gnw5Xf(U(@DVg38<~)VuuPP66)r)96#yB*ewIh z7}feC^REo{XFxaouUn8tfv+=J4w_UkL zSgQlD-4gD^TOFB0k7tuLwjWAJFtVV=jJDq+eT$W?WL*T?w2;|y!+4Yk3D(}~Y@8-5+5hv|h%=e4M z`GU!%vjd`0YbVmT+)I8HX};F;+PdY`%C&&EQKaW>N{0a_O)r%%FZSSf~%P>xaCl2p>6-HTj3`) z(`*gXko=nG|!ohtw?hMzYIA`W(5V=WI>??=q_UYNwXfsl%O3Hu#~ zwo2BvJH(X$o8}?RvcOll!%(6c8OwWv*iyp`tB$vW%n6>aZ)i+-}$eWseboMf4NgVunqL9BQEL|r+RIw-QYBg zUgaz1duy7#_2!RE=|MeZXrj*Bh!Hk|Jp?0d@}ky6)wFC5J^U=bkONh0dS zHy?U%H!)_G2@hVrYw1alNmo>3?nX_s+11g6h9nilOvz8|$>8d7WCt1#5)Vj9A_ehs2!z(|vbiz#>>02nrUxSO~l zcbFukQ&>VYoO_a|HK`2kutn~9?p|4q>?DjO4~_2)klV|1N=99j+-tu2!axj7cY*PK z>-w9J3+q+8HYw2)UeQCX^x%1WCe2c~e4PBK;S=SQ8zY^58bf^(j{-|?YH+_Va{FL= zdgQHV4!T&Dw^AAPY!r$zxqjh5L%N@bLsv%UFC5AA_CW2cyuvi{ru*Ut+pMR<^oZz# zx2^)Cvz1E@a1j6LX5IHLebL-(b6RUs(-#gpcDP*c+s`dOt4uZMMB!Lq(KTDzY$es( z=%1aY$vd-95rjfLwaiFTwa#+9YR^ONbIKl%Oh4@_@6DFz>J09E7qR-X(3?b9F)zI~ zt7h5pj4jIp#&_4t31DDP&tn7o9Q3CmXQNUpRJeBs`~#ufF5}B2sYP(%b6&8g4QlyL znm|0kx?2|j>>S5z7t$Z*dGj=Wsvw80Z}t)+d-M_d5y(AQ?JAyUqz#uAy)%Q=SoEG# z**4D|ukfxZ>zJJaZ~*@1sbc*n@)fGWt)@3`Yl^3gJU(Xzv$t9wTs_sT=QrEEvo%Ge z^m?i+OZEDnvjDUu`htPn)F%sU+ApHby>6yQh`&2EkJQ$_r@hcrRajd%4QG+YxgJW!4-8B_+WJUxdJN7iiMuR6`!-~;_;8eoV(MmNY+Z*$I(w)Z7 zzbR;Bs;-H_@x8B#Uz4u{ZLGzukHWNNFe*Aqai;()h46Y&|72qk6+f9jJZ?PVtQWET z(DaJ+t?`KLTzX*4n@3lvCd0-M(<1AHj%BX_9rON)$|Uon&(%BoRG2cWb={JIEKT6A zRX-uYp51yS8VBmM)x@+Az6ZI>+wqkg-pF<9O`?Airb4ukA~h5S)JV!UIvWm)wxVr7 zqHCWsmZ2+*NdfbOx{<5>W($L1y`q`?`Ek^BlXsS(B5;L*Afh&w082QPlz!n*^U3Ec z-dRX&>btb}WzI_EO#4MGM>|YqPIgn$zFaDd*0!J7#N=WLuS#UdW`(S+u?{dQ6CER3 zBR9|!zWbc?Ih3xT_NB?lP2SSXP%^hE`^8PDQHXGBa&EtPuNHXv<1+&Lo^|I-Dd!jE zU&~0(34D-r>7|SF&$Kv9u+JoA6nj)MC>f?2-oh8DWe4M-sEb=v_2!uI9a3k1Hnshy;c%xybO)T(`y{bj()W|q=i-~k8mW0Jd~ zo}8S{@OguW>eH$LX869_m_(E2Zx>%he0Q!GLykV59eP(Hx|EPSX~kkBTu3d+cj8b% zha+`S_?1Qju5U--oy+Er z<-0YU%ASQQXcUy{Np~%Lng+u%qdXT0lE^P_?VC^w-2SSi960F_XeR!lfdOh~y0lwD zjB?pL@#5AFb-nql&r*)Dr0)8_ypJsVMIU}hNeiwcg!;a-(uoenG_fwWd#kxL+xl|! zc&lsAuuW+-%Fdj$fnX*Jyth&;)sq$3SZ7An5!$8qlRXCOuK~PWRboe>CGvq;ByB+6 zU8aS`!-cG8>qVU#f1Zc|W<2d@B@ISWnp!yNc}FDxD&2U&;7-Ou(drhPJLH{=3GV); z%lQ7v@VygBiv6qyl1^W4l~6w~Ibu+=brYWdbROeI`xffU&w8nn_ov7sbV{EW={`9XZtcrE5S^%hbay zJYo~bD?excu9@!vK(nnkS~xPS3Gv>uzxSmQ8R5N zf|#|im8U!FIEI`NY{SKZY!+vCdZov=K?BlLyEz1(!y3*F>iSWvo5DR+1G$r-idxjKx)ng>8nO>L z3p8kUuR}vFNd0MM(51EtdemR}L@;qmlNzpLj{nOr%_esL^k}&H^fPjwK!U$qx0$D6 zMfhpkh-=GNT~|`;;(f}`NMt9Aj|gn0v+re3q@ZmQRtI4BzB^@`zk20vlW(>!V}`f~ z$vWt=F9}Ao3{zgad;*c0gY4d7X%rYpT|LqhN}5r8S;D=|#d7u(50g6oUikBf>lL8O zy}}~zt})^8)B5uJJ@=bu7OE6kX8X*E4CvOwp@93N8t_ZZZHCIu46AY5fiA`qg#uL= zAuwDRvYt(Nm?kAAtKuxyEy);4s5s-@L&0{YR?^(mQ!cwGY9vf6awd$xSfGmKuNJX0 z`SW>|`=A`kmhZ6JF0!L*IxHOYP%GM&!)`R#ob?`JpyC0-h+)^y*eUfY%hZ_@o43y7 zmwen=l6D^E^j4-a<5np$9I5NRO(WR@vMcTChzw5+*@{(lmi_Zy{$ICAEdo6#OkdTV z@4f<29+)w;tZOWHVGI}iX*&|PuvGMsac3uj0!WS9=F4h|{b@VTlK4H`p8F!^atkzR z^52?l^`7-%kQbmp*3=UP`So|;;FcGN87Qb}nuh`10_ASPXY8jV1 zKZaG`N1LOjSDmU(bi(e-+@3;c=zTO&R2lqKT4(h;?`KodTHw#x=h_k~@b}@n*CbF|ZU-DiHq=q7j|L?X?)c=q?zH!H4A*c|n9}zUDblcJzG- z;~N%0*{0C-IIjh1hDTzIXIj|i#qiARB5QY)ow2!h3E-e4$EIf_vl@@6CkwhYpO(tz zuEA&#(H!t{+xvM>>vnZ;viS`J3g`%9V~eJ<8GJdY2!XELEhd3J`tkI~EqQJl>zBy)L$j)J2CoC3 z`>;Byz4&7lul_C5=@g=luWFT0?2j!eR2mR&!-1N=?y*JffeHFA{`ev|tu*-r;~A3icp%6S6|-x; zQjj2N)R71)I+T<$ky6m9kS+K5>6M9G4T2RKeGBFze>TYOZ0Eh%`{Tz#fo``*ZII%S>J2Q#Vw z1k=gpj>})FA2%svhJ4W~-9r1FaC_EtGkdAfxotwI>w`4t&)7niO${u54%F&&#dGeX zogrjl&Nq2%Yc(bBoZ{0Kxbg1Zc7F!wrS zD{qF;k!E0~vf6q0H9#ozbeE{xgx!_0xBR z@Z`7Sm(Hqii|ZL_hw%|RzAVNHB9m36%^M`>;;LIkU;YuI=e0ScgNXnsaQh}s%5m7f zue~X3S_DpKoBfKq-wJHd3$djY_c4M_PNy+XF#8}-^=hGiXIA0LN)xy+I;43 zZKecy5WB1BL5xpDSz}-zrYXO(({~6R(H=cdtArD6I@}CGIm+Eb z^Mn<18h`2WaE5+!hCW;%W9B+{oF}yNT}>po-?PPjSi4GFJ>uPUCTOE77L6O4$ie}T zIP8#hRLE)Pq@2#UtLn}w4Fbxo5{AP!x4SEnMLWT_h#|#c$fbf159~+t5p3%j8lCi{ z>DWb%dLf+0@DpeJ0E=vu9`n6O6-r6u2@f%IR$ zNz*rz;hKxlxF_Jg5WV_wCo<>rQIF*PsGC^cq_1ew-Fx05cg!Zz&Tik*Zg+{M+Y7(F z+#kn^z22*+ryTJ37QF6tQ=3$4j#g0gR^6JwwXv&I`!9S-Mt+9Ws6n!~^+c)n#6Umu zjk?V^(_jLV-g6-)!k$I@NUDA4dG7t#Aw76xa))HYC;nOMNKM4>Qz6*E)Afv*VIGK6 zZR_QQ7)v0Kcphk7jX{A?Fh_8q=5zC#)zQHc3Qg}9xjK3ztFrL?^YvtKTzpq7@5qB$ z(+*mCQ0;p`_suG$b+8~Fgb7#V+)9HPXUC5^QE{({kKXs$2){1etzMHX-cJ@l!E02Nl2Dx2o4iB_P!~=sbCel+ZSEDDWLVV%(>U8x14l0z&22j_b z2KS|x$6X9DonBJaB&U<*-lZBg4=>#JcD#v!f@Q$Guq*_VGFBU&g|Jhf%tCPAMuMc9 zhtvrl#qg$~@0F{vikO5dW=6f5{MZd=qA}mqqpyHEL&(|q8|$(6NBj%wk)?dBQsy2c zV#G$4P5%tnXj|S_d+D78o%Pp861Dt>9vsfb?Gf&WSw+v2#POdHnw{NRt7?djfc)=Q zGjdA?EHh4GBJI4(lF()Rwrz&z@)ZI|)DPA^IIkze)~vd3`IYOHF~a^o&w6vQU1GTknx-h)_+2;dHtw zFR8q0hkbr;((X{F?U&gLa)W3_NTH=BhYgs995t- z_hEVAW1%G1B0MT2Q5cS`o?|hLWFxf+NX~v=%Sp7!_DVO_mqb~47dS==zcq)FG9$pR zl27u6sZ?2J1g};|E21zn8=v~T>OK@zZ8TTv_aF;Rv&S%)(N&7lTwA>tKB7x!DUSj_ zrpuvyglD9vl}2o4Zl54K_x3e0jl!x&^5um^4XJfBYwze${3SNC+akV}GFbU2^~;>1 zVU`w2<5h(bv3>U@Z`s8Ynwwd!Lgi`@LQws~exp(i*+24?5#gP~ognSJrV~hQt9E5e zXl=z8xBGc)U5%w+t)&lCMlC&3@JqB=^q2@1xkCC)%`DUk&u~^5wy01(q3wH-CbRuw zB>lB6EnhQblnu^8YPZoTRJyF$e1l^zbK=gM6K#e`mS6G`L!VSbOPJbt50R3shOtIAs4N*Mnx{q$70f+SXoFSX z4y-bj>0aWxYLK__gk@1PI^=!oGZzh+#QDD4ovNSOylzUBV)`4k$a=Wz)tLLbyFJ>% zs}W=nDGRb0JrRp&Q%MPeYa012w@(5xp#o%b_S9EhBI?T5I|Aq;S;{&i@$`z0NAU)# z9JYUfEED&aE5h|!vcyKM{PXTT_InQA)%en_p!8?>#gQI8k;t5-L`t5J531bprCIGQ zhgUeW1PlsoVVs3?`PbRcOgWtpkb-{DHj4%J2I#ZuxU%*j^I`QWxAeZs&*pT-gzHK$ z?T(JIO0VSihpeu1b6%j7jb(QRgZU!x!c}Pk6l+ z0TSzTd&zcJhophK-WXb}?sO^;$JAoAs6MP}ey_V(6`P?gQ;PGLi+3K3fd!)px+|$3 z(yGQq+K`?OyGh>NwTNZn~^xgXIsaw@9CnyWdpB(o|Z=_$n8kLp6@UK_a0vV|Dne ztae)srv*#PI;O&Brs-5y35!aHdB5U=r`f9Vf;uO;)t$0yJfclCS-sOOAsE}k0;4pf ziDF!)2X_!&kf$hzO&`mdNjfDjddkFF<<1&`S16&3bQ)$M>d4f14VxCKL;%FkFaS70FF_!rL{04 zsVFgfFX$lKL`1AV;r*p_*~>;3^u#Bh+*u`Up;fMlzQsW9b5Zs|QZFXu&gsVh6m z;(l!|DjSqZxe&!}oZMft1@Vfpho?R!r8@vT9M&8jR)^Sgi(CiOy=^3S-7Elg!zhh%^9TU zMt{W?4MybT?2HLl-O1kbrzyBs95JL}RFk5DlAv*)c-U4pEZ=;4^dY9W@xorg!!`Yu z1QR&1#`#plO1sv2iWd8>UJZqeDNOET6rT18IG1AEM@~j7{C?5gnldLK_*7!zBaJZSr}BUA*m^9DmBq zn4ADZQ#NH@Jo}>O(p9|y`SqyHKPS)7d!TI7WDC!*)f@IfGsz_jS)d3*<-H>5=$^8L zO|M6Sui6Nd0$C+JGm`}NLq|h>4k&U1+>W8r=IDREP^Q~77;NK9ch;v?ZJ^nL6#SBT zS!jK^K#hrm7n@$K`8TUm*|>pDOk;h6U6@u>|GOe>dcc_kgQ3+u&@B5>7d#H5(kyu4u#L|?R}LdK>L@g~8Er*fh+*g4o1%gJ+%4f(wriU>t+ngXrW6?W1!2->)QBL>v@v zOGudNhNu?js-~l{RPg)h4IfImP1gj6`^%#@?sptMh_{_EhYbvTc9E+MO~_NS_3i&x zjn=8wDA7NvL8(91x^r!Y6+^4Zrr)TwL^GgSx)mgnrP}yLZSXNNvqpKrSs{Y%T|i@y z8*{}wA}lAzwK)4PMK&!{)bp`iZymciRY-G%nnEc&@)eTQ(I2K1+k6ElHW~|Rk;F?Q z>a%*}-4w-4*%AHr9j=bS3(18&a#t9uJ7cT{1ITjnf+GVNt{qq*Mg51>dc?6>(Y5D@ zTBEgQ+ia*U^#DeaeqsGAr4fVI5GUr0)uI91^N;wYv6!q5&2=oP03?_3RFE(4zFUWH z=t6df*zGa-fP{+s%WA~PCvYwUUkh&DDw3!;ipvTU)7@ z_&FBq_rVw|>z0%w4V@P^M|nns%9PNLy_F|(l#2!7Q4zy*Gug>7rAhE9V#o3c=^oWW zktS|fbhFkl^Tb`x1wxEy=y&V9q!fr(7w*=uFrj* zsSGpBu+r$1ry9zVEm8gs?L^jTbiAtECr_-t4`qDFWQe}6-G;sItHq!*{yNl#Cj@f z+Kon4W=BrvpqxYhPF!Qg9(JB|u=O}CT}W}bR!JEhdt7r47kL-t)R&zlhrZwW z;E)B~^d2YY)?EoejDk|mcX2HXNEgq$cklBVNy-U6(FiC73oJxO^y>#1^}*h$vdi_k zW=)2R;oqTJYr<4NX~N$b+J;Hb+}O0~2NGsotD?)QwH)H#EBD9ppDUD5k(}pVJ+(TU zV@9zd-b%Xi6s5NE5)!pqpcyRiB=j z`$U6hma1U&Zlfyt;H6Q2^oWhsIXgYRP=hi0YgZlj!7>uTZc2Np~XSYve$ zzDRygE#M+Mo2(4A1q^m3_c1p4YHCxS>L=8hA*=;2%TWH;fC51->yXu=m0@coAgz5zJS3^Ekdg6(nZ_@VrIoc8U{+k%x^&0A2T9-bWJA` zYA9k?t81!tf>K8=A3XmV!T%CL`p&7w_qpZOOc{#t&xP#mZMeOE_#YiD6HP}R>px7f zX+>`)=Cz9-Z!1a{gsv=+uRsUcz9}n|eq6vcaSo@1trf@+A0t1bm#zwKX~xd#uSB^E zZ-Y6QR3|#R%~#l`3W7IP*tXVDoO z-3MWG=a|qrNbLFHLIZBDooO8c(>`z)RyHFX^qnHi?Xzkr7c`Xo8Pkom$8q@|vT#It zE#~YfuYC4zin#WkH;#{$^(mY+@Gv6RC*I|8%ZF>C&EZn63op+tJOqg{hf`mhzf?Vv ztbSUc%RR6CppmK?NBVMbKq`hbmC;z6_Er4x-?jopPqbQL0aHAWi{`nz`++J2xiig5NiY$n>y5k^blmhrP!> zc@=EgqI11fOJ8Nu;$lB6AsfVC%kDoHBt0rb2*SXMS%SVZAcN^&9<+Zrey6iDR9r5lZr?Ik% zg{QyXg=`5Rso}a?koDn(Y@Y{Smmbd7J*~eW`B`H|n}(J;Db_`WnOb|;t^lJEKPL~- zz$kfOIhW-p!5k^k{8>oS!|{g~=gkd7?Pt)cO!-;%QG$mvqh}tadM$_jT;c*PDBVu8_MY8qMDyU9A)VKHaVZo`3PRJ!B}!JSIfTRjDCe%GcTZqh$-H@RTS?uM-5so9S} z#!*>IAB4hJqRq8nI%Jm!qQ&47`$@SKWfq!gD95P{qIU)Jfh7Ajxt6D2Q!2reVsPhO3gOEPA33o)9U-@r1@yPCa}#9&H{)Nv+|# z$r7~m%NME8o2#At(0=w|6HtU%8APGrqv0%HPIekc27hIfOR=d{&=0GOTY)^#-bx+0 zG`7*hEopwedc~TH26HG>m0EWG##ENw7_3+W;n7{$<6j$PG=67f z#0O4eQj!T>H~8!CQ)IORL;~ZhlpLnGUi5bten6(FOzYy_NRM#qL>wY;lzv8TXfLJG zsJkmd7X5IMM^l0XHyiJ?gZo%Ev@dUTgFE%%D@NqcFJ>h2!;@U@=8w7t8Nrx|icvSI zs1H)gX;>)8iol|oyC1LRv0Aw9j%ZiGYb#)AjC0lJ^oP3d18pTt(@ZoMPz}lNk0Z2_ z#on6atS2`MK0}JS;UvwbXHF3AGlNYudj2)dG%;Vl|Mqcx#sc4*4!Q`G!z*%e)eAX1 zHYM=U3GXMZ1Kqa{z;wHVQgIEl**39E8-ka!W0V7gd(})+%N-6vo_`qCf$}Y$p$P0Z z@>dcMnRS7L4Qq|k>A9r1EMyGx=xaicZQL%eZX_U7AwVP5*67RlX1D)pk?2|%%h<-( z0k>3cx>1n-o)@!St#Ror19Sd#eI6}rrHn!99^8)?Q&u~+zIZwrr!9r9__ zb_IPdvAhqryIRc>$Juh6W_0lpzxNLT$gI@Wvrh30i=i=!sSj4N4Poc`!rLwf%B`xy z>&|(hT(p+y)tbVdeD-^OrASw;RU4;N9dCYJxaVRc(?)Yx5!xXtXOtaWh_c5nmIjE} ztiRr^BW9SY7wD)YoR1G#NDr=9H;G&q*NUba(3E06^?g3_foL3KnJs(H_0FR1)4d76 zR{_Jna>Z&jrsv+}FKBn0Npt;G>UFQZ@NGi}QtZW(cbPfgU#wR(Ff<$W40gCr7~*B7+ioNu+sI4#_q z;Lj7@m|cB7Qe&Jb87@_%(ibn(ZVwsrp{gWe&>r4j-k;Q`UC^P&a@eAS`G5s$hx@AG z$KH2G0`qz_>uMzVdRy{(3P8Ot^`ksSmMsloFAuKk%@0_jIv!zsnmw#`& z@F7!syC+@IzLa2B%^WE@$Km}vjY&2Yo&hlq~4?3IgvkE+%4R5&R7kj zT$Melc|e34UaH?q*7H|7B&$?pbMCr~JjkiEpW<;vHrj^gui01oRCmWK%nA2ZyUzSF z?}e`&?IXfgt1+2>`aziTQCRsXqkTQI&S|bIiLq?C!>CRr^4i-YX9}}dHCL-`?Sb#< z89IFl?rqarZ6KYgv0yx%X`u?ke2kU|Z{&f#o=Ri4N`X0ch6K9CF1wE`R*m!K<)}w% zG>haU57Jw--il1xFg*p#!qk};Phe6EO)EKb6v9~vmBk}>fThN>l*T0MB~t?BF1cYq zEAgQLf#>{2VHoj4y)4E7jCcz7!pnTV#)4$4_<&O9cadNTM6E@HbS`&T9@t2frz|9;X!naK;Sgs!Orv-k#?mKk&q!I`HfA;BI zz~bBgwe`=&ock+-sT>(E8`}WtwL7T2r# zEWiBEQF`_oD+W1Uv%Qx?S6C$71gJ0&*ks|uyW&=`Y;Ur>%zx@ha`Ddwv+Q>>{w>fE zufmrVob)xKGarBNu~)Xpjdzk$vj0h6F`a=|FXIRzJC`xD~XIb=wRK4(>mY zz1xa6G?#N_A7tAr?^oeNzE(cP+*i5h>2G7o#s_~}+towi#-7|xyJ=M=>)AQtwY`t$ z?-e9+!*UuJ9eG!v|5oWW7H;3!T`)BABG%FV!uQ>K1CL;Q1XvkpkiD#UdUBtNr`0)` z8^3Qp)C*j0xI5b&!hjUSWx0+1cuCa|3Q(03_H` z`;zV)?!Ii;ci9yohu*Sie=l0^O7-#m|5<=mcdN-V#nSzLMq^8c<0>0eocf>Ey!@Xo z!6L)@i?9fAcej=Uv;##}wRn7?-xpeZ^tLRcjO_2bSFlL39lC|A`I~w+y-hv);g&3?W};!mpZ;{D4_uqV?#RD+uSb}}=D<9&dYqPZ9Tj&qX74jnd;*=3FrK0*hg zefNgt3YLf7Tj@;l{PPblD^40mRWr8yzWG;i;PTx;D->*R1;!EZq;vb%zE@z^^TF~RRsst!loX!X8r*UyCaBS!qvPu;=*Cp8_`}HMjQ*YNZcN>q`l-oDqIa->Jx3jwCL_yzPhBK^X>=C*3M;SBx}58`G+1y-TVxzpOuL9 z4o40V_v2stN#muvEZyJbjl#ww$2y98nOOJN7Ho>I*m zUAL(hHpPCztCAef(P#FjbzH{&5{Dd*K7;Kf4wkzrQVO5~1+V6}rP)d`lDvm*{aJ)D zWsXDBb+ZQL(K+>Dnxb5~*B6}=uID=ltY4hx!Oio;#=BuoSU67hiVr|-D3Dv3F>aY0 z7|QxGIL25V^2IR4=WksZdp)6dia{`DFQ^vhpEsdVYeye<0ZS3KRFTbTLJeFG7+~q zP>?&jT0vvscp7VaNG_{~bJ>)2bqfF)r^kNFjJ~Nvh2HDT2R09SQ7T(5iwp()B4e8j zatOv^bpIgf{wavDDx?EeQR&2UJl!iz39k2t#OyCR!5_r*?FVsQ(Le^WMzK00am_o+ z;#>3Q5M0Z*D+{*R>%fk=Npd!@xV>1eUTDn*zL(kE*v-k-7t4iVvT(G%hs}@Ki=CVe zJOG7g3?}&lz_3EEEFJ}OXxwwf-**QfDT&@&!=!@khf)owu;-B&oi=0I_N6iUKb@`k z_F${|z-cDzh@9DsTqS@hAEghrs>w+|wj9Ib@TLs@0f74AEe6P-v7jxm1>E+c&um_e zQ?FYiQsOl-5N@6O<1z5Jxl6#g{YX>KeCfi(!e@N9``=P>-S>JtX4oZtlWMj(9v=PZ zKBs~KP9R2Qj6&xs=%Qvtz~PU;K-m!ptj9rWGcx@re1!_F0Z0p&^~wE%EWlqCQ;QY# z1#EpzN<<0o$};aZBdI;v7b0w37>C_*_IKH9#{snNlV2Q$ExG{NE6i=g6U;+PKihc9 zedYs~@~b=}77jddI&3O)0JuPj#kfpJw=a=E5~YoDJ?46hb-yVf5-fCHTADM8O=54F zU`ztOy_?KbUguWZ?dH1`5C1Zbj(e_PtbDK zNpb~02Tc*X&bW8;{?9n}mnGLbf1M>~W@rCp%Yh?5{zUyP&tE{XNdM*hw>g<^TgvoV zGyTsPvkQp&PAzNXthUo(^L)7S80HJ9b%JraDuJ<(;9X%cvv~b+9|yGL1i6nIG|Q1zBaxa@h3)$*R$&}Wty6Pr0zx&9{amJ>|$^-A@Gc_17-`M}WRKPfwhu zFDSAad;yqS==}32;lr@2=6b}B>Q}GEdGv{$?fdQ=zg18@uOC^s)LaK+FH{fwDq{I7 zt!v~>lYbxXxkX>!stS;}4eiNm3m0zBOy=?l?kTJb`NopWW&!hf`i4s^U=1ie za`)*2?D#sY|6KkoS0UMV(+V&~dThY&N^;9>cQf3W=g6XG6=uKfP2!=3Y zbQ=X-_yK=riSK=tFCC1P{u9d;81tM@L?Jk!Ph=1|IA69Ox1%%@=*7hXLBDDXvIK8c z(pco{)gab-==b7IX9K_W#1rI1$0dX{gZpbhTgNi!`=E<2!X2`79n?k|;3l~f;;S1ldSz8M3X8rGj|lrdCRM0m{WY}WC^r@b#sPttpDy4S{U{+c51 zV}#3Bm|-0?Dta2^ZC||ksh2uZ&B|(U_P(~=XZjoPcET^k>$ljgc`at0dn@y@pY&^( zDq6FrR?8Mfw#+y4PKV*A?GP_2A3IkE)ELo`3mP6eJxcG*>_7x`f80f&G8t+B4D!!j zWx)81^H8Ij5*R=6K3lh7)G#(IRGTplUsq>2Dy)~MGRUtMY^Bod3lEn)gXzfJKPGfI z)KA?gG0Z{7jZ;7ICc@^j%!tTRTEFH9AIB37aQ-9Hm0xmX&ZeOA<7vDW+sO!{WBlw3 za5B(}B5hu-P^c1q3~JzcVKa1EJ+=tqFlUFb8-XRk@}<6b-2DI^CS8GYdAxbYA%KHC z7+b7@*44^>{imWxX+u^UAJco+4qF1sHA1ekL4%P^b8lIZeRLDK02G5=q~s)J%LWF|q~d5^cNIr#-7Hy&88K(8Ryiz{LyDfnvvZh^k{p6W93#)eaoyK4F7XvA z^%lVZMs81{rJ4Vohzl^jfg(v?(=f})aKZ+GeBK9Nzu`z&yeQdsAc|FGKHJQ#n=>Au z$baa{LV?%pXIgjLF$1=8!%?@EBRgw|Uqu!;0T?K=k*rkX5A0CF2|#CjkC431A*Uuc zzK^!aswVomvNqXo{(Cv%B zQ;`{tYsB{w`}%;qb!?Nbswe1WoriseMR`NueO1||F5yFVCpmmrCiNVpzSn95zTcFr zn3)+hvrTJ}HDz|uG#_O7x_HKl^OW?{yV-($S;6*e&Vwu<>D8hp#;5@f8BPjO;q$CW ztlUGu?_jA;tMTdU;0ESBos%_zz^*AYfTu6cv*BjhVozc20I|nW5NuBe>K5o-RK>>& zA6`QBpZ5vI@~bV~=2RklBz3Wia+w8ZddI@Ub6)v1s$qaf2w75U8Aadv1U9^RTmdAt&8!}BW zGRm9L2761A-?-taBKm1eDT-}B3dkZm$blMUZx_^JhcMXhkOm`N)sfWbx1hK>SCo79 zH%M+RA&dk~w+AV&1V^Hue89KGvN`(Uqan{m9n2I~t;0lg!t9|z)=y)y6$dgjp+EuX z{5~qkVXE~Y8Gz6?l4HHn?$Ebn)8PeXaM} z;7of^@|xX(C1a0}?!51dG&M^S&6r=tQ$Rc18$F= zsq9cP^<|8L(7$}O&^vg};c4{ zwi{XQ+SFoY;Z$%P!U3RQ7wttd?5b`6yX=fi5RSHHOcJ3M&_pPyyDYXDqy zI0&8=ku>@$3e-t%girjn-yZ8!-^6s=aVoh5*a=G^D>>UW#-eXE5-XB|}(nW}-rKFP)JcpFS?(lp z8d8a35~bW~2dAD5)Smyt1!TO|uJb0jTMjQ;WJZff8kQDZw$qv|k0AqZo6( z|LfvdT8c6Mticgu8fmUrOajN%k-|YLnU|KE@w>8%S<5={9cca|d&suzyYERy{g^bG zrPeaiFSzmp2j}Pgfo(EEOdPe#>WQ)a(XH=#CZC_{Tlw-6R{L|s4qw(a?N%~i5WG#< zPnPhJ$Dw`SI0cRD=W&u%{1BQJ6umcznQsZQUTcb!j+^FC?wq=f@F`;7BV}!%(i>;P zbDBryXhPd;$DN|1*h8j@xoh; z@2;I~vGqHKm5Jc7W{?96S$`VZrBn%$XOS2p%&i~C}Cm9g!(l9hyY*(Mo z)yGV`m_>T@FNFy|C|No&axpw~1(~pPwYJ{%7kt-k&;{9zub*_{7q|8=F6G z_pJK+Yzl!n?SKLc7QJvHue;G@!RJE4C%b8E~iVY{_sBXLe)~&W=C1&c$00BaWN6hTw z^i69O7 z@FXYj9d)(*aTV6d)5KJvC?$^1`iYCn#3AR(fKN3?xa9qs{1et5=EzwhTY(%S_gyVo z{vvU7m-y41rQO+i0() z>!|xN(NDj0d8JMoUd=;DeN-&9Qy_$(1DGh$Uo(T})ag1VqZ(wbSI!tVuPMfkX`?+kd*clMw&C<8%VqN3G=9U;!uUiQdO92kNJ^`NPXYPegZb%sI>U!PP!}o82 z-OP8p}N z*HPKq;WfF+qqjpPF-2c)|4h*#oC%OxdQet;yH+-SGh2u?NW4C}p4C>KCX>`iY@`Gk zaF#Y3iYOkHUbG2<;Uv+#iu>3gP0swl19pu2elG<6Z~ereNDN?XXCF?tk~n6 zk!z&1!W%#JvT4qr4M`@`wym=K{jDV)pDXs_eQZCU0mgR z!_`g?Zh%!{C2^Vub7{n0K?1}LIPuI*U9)_PwhK6ZkelWh)FJGFs>I#(5@}T% z>$ILXbt2=CV`iMek*DyIb#EamHAQQ>*5${8GK$Zijfu_#waHC$8$QLDcb=~wF;QSs z9(D(d2dxXCBo}DWr4ZO7S}T$fj#ENyA^iFCC)W? zUz?@P=lz?;J8g)aj-cfmc&2i^yRY53`mho9*VT=ih1!>6gA)wyH8&aKE~#8=F(;?6 z-0vt#(0@#cv3o0(>zp=RWfCUM&Bd&wyLK4Fdz0@&Ei-CZQW_4Y&kUMd1&3k zYlkLT&EE*2{brHvsZGLD_7T$5ighVI9wYwkvGX_Inn{HfYOB1-q8n4rJyB~K<-kLK zykx7mX`a$gFT{CMyY4&XDuBWs1ZY46MpZsa`J9KXqVwy&?3X&@^0PWux%JYw4ENj3 zj~1KUTNmo!zmi3f!fg+j)moSO^_7vALSzF6m5g9&LU&;KASov8(fSE~g!*b2kb0E5 ziiz+>wu7=8-ZY*xdHjN$)fb>~4YX42R|%QTd@^0#o3{;B@7URT2GOaC%G{jfrQ?%} z$Gu?2-H#+zi%TtD%G8Rmj#Tq2fN`0>Wrn5ruf#RXn-F;%XU;i(HI3P-=~`Q%QO3D4 z!&RnE4~Fgnv3BfwX_uJWPopK+=tt}=0wg#8;6*{2O_*)T8FK8#P*n4XT6*dlMFyxP zb{d&ZN~M~n%~A}lmpnxA$#xooGt8~+ zVXKBaULA3Cqmk`{!Pp`+asi-NYq&p&x?&HWl)LHCA>KK4hTNiVdemj*AZ-XuOUBoC zD^0LygZHF+!iTd=Lw!OToA2dG4u55>d*mmMFe!22q2p9HO3i?Xn{nvnbr3IUl&-Xv zC`*v<$(5ltMj%pI>RRxx9Z2c^>}Hrbxp?K^H;yi^8uzBkn%2z#xQ!xn7TBYmQ|(=D0wR{<3t=zCa!1Blm{-I*pXX54ZI%BbkpT z)MRyXE?8ywfn_h(As73vh-IENlD(>KMl|_g}FwBOG-K;ep`X5OqeFOEHInd0)Giy8SA`N z8WgH;c3E0^-2vV~l8T<|DBmh(kJZc^D=3*QKMlZtv3_*}u z3*iuL;)vBvS4zn+zrn;v@0Bxb-#^e;2ir0_E*67#7XH2QOCL<#cXV8(E!_;+xF}YdLh3L`QkOmJRy}t9@wR6Az_*@lfOPw!+rZW!bdNiZJulWFZn@)_MtUaBvjk|y z6i09sd$U_QNu=dfM=hTcEM70KU#whBhRyNojoNhMF5#;l@UGkIV}K4t_fVPEWkX!| z@)-;^VKxtSl&kHD;RW+QU&+7~s7Rgf-+DPm{XeYW@8GFhO_utJ9hzFY#dS zBOhJwy%cto&gKe~vXP8S?u8-bTT1RImT@NQFiIgUk^}6(mf3j}j>$h@ZYI!W@qG-p z)yzA} zJ4))$LkHrmHSqmRGWYOBZhgLN3dmC+@3UJiFm%Ylcxof|CF`{}96N^W)+5#wi2CS9r&V*e>!u9acq(BGdi5h6f@cG!WQ@9Gd`yMa+GEiB)$QX*hZAsw|w zD4~vT3Yfl%f-Rk#XRadPrVy#4pi*Q)@KU}02&V0&^GE{eWR7NaK}rVugU=W#Ui_O` zIYO%P;D>qObc_Lh*CNQK*3FLbxAPByXc(Vc^LpoXzUSq36SUl1Q*vm|cM5MyO@{UD zQuNKG>^3g_5-wh^JF+>kGI9-n0`AT=-IHA%5T9fuHS(pT`uM17hdug9QZmBX8jy(P zX-J9#09$}o``qVmtbmiHcV=VZ8?S!+_Al;RWjMV`iI{IlJ*D|IXJGENGYZL-!Bv|U zVrBOO=DgKho#E~C>~{J>@0~U>h@qKf@Ed03Fg;nSY{&l;W#ffUV;fE=j8aEmQmZ3$ zYj@zR91|d_3p%oaFlNWDuMty0M8WKz4+eD-Ov~nG`I-foq72WR;*5CFn@UAXk3Dh| z<_=`Dje0b7o_(K3v)*?S7r#_3VOZ4#V-IA@cnfqazLc9OmSy#5iTtwd!5SU}^3e%$ zt*+w*fZg`(n1}rHGc`lD{ca(^gnDtz*;?WIVm+9+!!%a*P+u3IcqQ z3c%}#wd+LR@rgnE%0EMDi}68=nq}}NDOa1zaC&Y{1gS`+;y{iIY=)P_jIK8?soIYO z{E9b4_00h7ljp2Dp7hh^eWVsS5*x+s+-7mLC0K4{gt;^_#!X&*uj1u2`x%m#ZqP;yUOZQ@CpJ-z!vfK=&bf>7?H=Kj*cXnUK*(gEn;!&Hs(A6DhuS!!RA zYG2z7kpI_YTIc9-*|T&7I@ICC-U+zGuIv0^4)JdoBI_0_!+^N5!yF2`I-lKNM^6S;Z_2rw2I6i%Z9Z{*;spKqdfJ?PH{E*Xhb8~t)! zkj?wElsr8cVS^PFJ#_1TvXr(d7(o9}Uvm1ot4VH(tT(;39uZX-osvM|dMqX_OUgz;3oCI4;tW2ZHbV($; zmUE_PdZx~)OXWBQm~n22aV2i0XetdT8~kp-w7%kv(ZE~MM#{Q10Z%pc@FA85gZbtoXOH*R=K)I)3H8V)UP(1S@4 zMq49$VT;O(Dh?a`9%P&HdeWOr^7n?|hhsM6+03%3x~~cIC#4bzvZ^M(Nkfkr26#GG zWl_#RDY893R5Hl~sM5lR*)0j%vGOLHP;L^D|OrVbXBZMjIYULCA zhIJ7t8`IpBErNi2M7U!XdnH_qoB^ygLQHHcW zgdrC3d}q2AC>Cs*>V6m{Ba5l4r*r0=#Ys?dN)t?V(3g9ZITejO=7^glHu$tL*QAz* z(~QQ3VRk_NGl(`(bm97L4B+VEE1$fAh$!mMi7w`@B|7EnJRVRK!#|yE_#lS&Bb`>N zENYw$assORAf2>nQSQ|)bK2QT`c)*@5#bJ!|PA<^))C9$_d` zk=XtTF~>|BiQ2q&BQ&xvA`m{;l1}ec%a7>0KTmL!Z|7@E1%&}q?B{U*7jnxI;~Ig_ zl=g%jj#m@`XEk`nX{9IVZ%+E*TJ9A_5o@#0GG{twSzUqgEz^dwb9FL=``@GoXz$O0 zeV&lBBa#+E+FqyzkzwDNq~-

    y)wlA=ySP00qmvfJpfTK0>Yny~b;vP93jCY+XaX zo&>a$28mc^KGZ#PRI-+?CM&s2<|T+&Zk{TDmIhw=pN|&O@WHx2$$a8A`bn+I&vrV> zy+wT~^;Mb%$(%MXRO8Klvm#QSQnog7$)d6Ee=VDI7Q4Dt6NF0gBf!MEx`ivC3WO^* zW3~PZE*Pd_G?=?Z3bDBZTdBLYk%)Fk0}<2?X%+vF_*pHiI3jMs5Wo}Yd_`M6u)%iN zw~U1SV3#q5E#(BI)AbGO6#yOhqD|Cj6#_4Sir)ZOYG`0^8}GlbW#&f4=nYv^SF~E^ zwEE_-zg>3EnNCHJ_WlZLAdSB=`S*NbmCb>%#e2MrId{PxxK}`KM?Koyo^pxTQ$C~%UVRE2U+{Y> z;&28WFsrFL`$`VibZ7Mn$P;CDizB5>v_LqyaO9@0a{Y3Wd!DleSjPl34Kfd@RfB02 zTSOBu^i^Ps&krvKUPiQX*XU{#8>>a2n%#eQJ+`Ppmh^olia^2#is2b^p2_xjdg>cK z9wuKRSjo7sE!?71R>8aThwWx-6M}v{ND-Dw;1wzoV`IFcI(hDHX9(3ijdHB&R=~0 zgb=nz^#9e(xKe(uFWF4EeGoVRE=VoREOU7OOraSzP&x&a#Qes?>`VI~2jOFg%c4=l zAy-Jv#UN%pZ?T{FvcE^-RgYF0Ls=s`DYV{cTOf+hCzbEx)VplLG%U;FLH{n=2Y7BCA$>=5ALVy+usv}12y8~Au zBN)GjSq^?u82JPvX}w@vAdx^zI&y7KLzuCRDi|1-w`bw!+lgVIAk`e6fLK!^4?a9l zy_sWrd;BLFp;U(o@k8vE3V+6%#cv_iPFM1g&kSsB0%ldFX2ydG-ZEuD!nJ1K1Vv1( z80uYl>`Qbc9r6fAQDjewC!39yhs|}MF8ez~DsfH+k6-5CFPmQq{M|1Rm%i2VbCl(d zE#^m(Yq!O$@iH*ZC3f7F#!q+y6gZXB9A~HC;yR5Ob#FS&sFH`UW_wTew`< zrCQlIAvwH~WiY;USPTHes4DjDVduTawgXS-EzSNibD&ABnZrC*cCD?kBR^@Boc&u^ z-HX7nR)h%8jaz7kl-2dL5)=AXZ4@s=`93-gY}#IR)7ot$(pCQ%vZN*Z_r(8J&0&~z zUR7keV2c&))uOIm`9VO}AaRv?F|ib$q{ zNAXc|FYPKCeej*@z)8Z;-bOzK+e8bwadcm*4UKXjsN<9R)4a5 z6QGz1wr@AZp0&!nmlLZ4f1J)an)^O;yTdSd%3r2?A3ty+Z__V_%vFGf4-|!KCNg0& zwywef*X;3E&bDs)mE}3V1*6uR5R2NPDDip$;QR#Tx~9c{$7*uRXDN5twPwe|EGJ~3 zeYU^3081#jV8P+{cG!MKK@X=BY$fbCwKH?0l+IB5vy|7d{FczikLc4(fUSt2+#CdG z=ekBS3793R6fofO##!{Ipvx7awX5VD6dFFL+V-0oj0l2mW zX=r-JEe#J&I#>arN9iDtHpOq59t&yZ)X2$*&pHPQ*Eui39Z-QJ*i>+`oD6~pqST$2 z6B|o&L#CBtbZlR)zsY!OvqUkFsxh=^Vlpj*)#;Vxfc4xgyDc3oKaNW|-?xC@r1z{V z>ttN&T&2Yy z@V7n6<&;4xM==Ewj5odv0#c%=QqG^GwJ3wiZoD=mt-?38yf+z6NIxEkm={ze1Y6xO7^-uFh8H<_jSqaP_IQjs(ycg_udfkIrv4VhpCDS8NdD&PJQxfRi{HJcuD+ z-D^UY;?l7#xfVFlY{9Hg2P;O5Ea+TS$WfD^D7@DrFy;I7A**=!>T2Qo+CHby{k0MX zg^#W19rS4oZlwi2P%4zd;?bKp57H7DOCe_>=CG8_d5m<86oMz8G7rFsQg`4Zv#tar zgt8H5`zTj^Y=Kp7>08i?WX1$(#Z~uVY^D}EOTL`LpX*m&yh36CozqMxQ+u)O$9v|T z98rqrI`E)Cb@GLKs+>3I4a+}&8ENs!iF+ez=;85|$`Z4=)ppRp(dOBlF@2gb5D?ow zbgMAC|I~69w@(h1Q#JLQkLc_fU*cpFKAbpHok`0RU_v`t#aGP_aLwc>VBZ{T-L)qn z0t!~G;{FhaqV%|*%~q8o zONLTr;{3dG*CkdA9?6K&>l>e;8kf89Qu7G%R@e2JpXJ=fIW)aS6@hl|%Ic3^Kc#QX zOYhm>+o3)+QN0n6P=0yI0=XV#J)3St{(!F3(kU?} z65!o4gOARaBYjjEQ}cvdv!&ZpQ7%=OlSYE)c#oKv7~usZrUUPZVv-Nsu>Qe#r3zBI z0;Ms7PJV27*$Ba8qoxE6Q_2yABx5e}V9eBBqmxOOs)i*rNU_LFM+3< z?DIlg?qo(c-Z%umq+fhvz73$;mAhv!W)w?Ycg{Mf4ZKbX;Dnm`)Mj01Z)eB+HB2m2wIXRV0B9JVn+{JOPLXMJE{A{T2Z6z zo8BO1kHv>QlR*y!r^2S~#cyTGgr#-N_~&ss?~P z*wg?zL+B@%rJp0Yj)rk+QO=Qk`BSYQSn#u=XLWkRzK+~E>5t?4%VKfg4Bc_-ydjV) zW{8%XhMix;R3lJAM;@l8=XHmO{0r4yo=UwGV4gBOx$!}2f@2Zq0u2O1G{=M$jebmZU%@YW-sO5<#F zZ_X?*fX0Uhw2lJ^^bEL4_YIi0Jx6Rs=aT;TKh?3qN3(h+T;wsWYs%BkTsy{786^< zYy~o~yJ}U2A$@yU?=qwnq2Zba zW{Pc=r|fi%rc?cV;=0h&R7-ZpBGZ`s;uRAb8Ss?Ptr-btRg*@8)9wVFO;i`f`NxFI zBWs=R=Em=5A6D%%w#&?aQF?;htn2ulYpb)IDtS<>DDmL)ayKa=rxaxwv$@(pSpaXU z%`-ZG%MP2z)9(j?*I$6wmlVJ&3b`mQaw`{{&=ygCjY!DmB4&5l%uYuU?NKNS0p&r~ zxV>S2TZ4_^0OYX-MI^FA3w{)O5DU>ppGm@$1t!%K>q6h;clKB|K492OAKx8L zy!>hc$tVOw)*=cbsh_;Uk$`ni+ar4RCFx86MNtN!9G)tp>ZO6JM24rYdb zgxk*ZE&r=jbsy3k6n`7mhtDsHOly=GsPYh)hLva;b z1iqN0+@{96_>@?LBKP$8(F!qcR!5*^-F|&kIbx`K*U)5ya%8!^yTd8G!ud#b%KoH{ z{6T1JtG4v>whWjhu1uNyG^=yJi>g$+z5|yYU9t*0}V+ zszpC0A6N&EoWqrI5Rf2>Z#=~Zv3m^m(O}&)Wh+VBEW?;b07bM5__)rVstN;CRXw<+ zLFT84Q!_DFcw$yl7{ygg1Gs^N$1qbXL5a&NMbKrw3k*g zHhgv1{hkb+YDly36IxZ5Tk~5Ec}gaMUcUpF^pwSEhzFrwr#xppEiVGws}KlGamvVD zJm-GyTaPMp7g<_e;88Kp;u1>W3SgwZwFNouKWU!UaePm3oNgO(zNSP<1F7tXkoEp$ z=IBnVX^w9jd%;83OafUt%`@gK!et#&IQv-8<>@+xjCqW>DQ3c#i~~2S6s&^v4Mi8A zh<3GW!AT-l>OP93a&Y9%EiMS_f$S+v`_@NAd_63qfG;VDfi>X$tCOA>J$R>*Y%hx7 zmPhFXK9%De(5Bqq449%twD2gE{-2n-Zef`uI%m5`Zj&ZP*LN$qIpyj2YlNC%APl~F zkvb!6({E3lX09yHHmYLIj)8?UTkSh$9w@~Eo(;7vZ7aG-CucqaGWs09{h8nA`?Qd$f^aOi+T}h;~vhy zk;Cf)zW2`2hT}UP_^MeAm-(0SQ0o+wOP)g=nKVPckJO=gM98Q6QhMbqkQEZI7DM}D zHw-QZzVF3lB`eQ%^!^wF{X<+vop{inWoDyc@Tt7`^t;9{3R+5;QiE;!X!cOf;=3)e z5b}qh#hbbsi{IMb^VFCJizVgu+5=pQ@P!h#V;1?NetOIY1vkXXG2@>*t7uGrJdQU# zC}&ehfK~j^qq#em@AR&wm*bP!JiB~^lEaPdk0d=*GOY?M{8RfRvJasNF<4i!%%)`N zOHt3JXXWmaZ3zF(}{FM)CF zAAHh!h`@0OFGHowG+#OUhg?g`VDkxhyU%8%Dn@0}4%@AU4;Lyzq2TiG$l7>@@<1$a z`Zo@MNp{7ObJ2RM?|OIEwp(+woin*i5b@~7qT*muzE5u93E?l0{=U?g@b>eaRUUU3 zyc)ZmkAwzg z*20+TUKv-Hu4{lP8p-)A1M)wa7WjcdF|PbS=H4@^>1S&jrh}psQF;@ks1yO|1W<&i zpoooLq!^@k2vtA@q<597D2Pgj(3|ukp(tH~5D2{!2npo~7;J#{C%Cr$ll-FY|h3H<#RRW_1~SA>Jac-V%ici7U-Gw8WHDe1|(|<;U%vaM^h` zsI=W=?EMQbb}35&pq`>L(8_NEq@4YSETLSo8X}KHrHT*49p2%YAJP1h!=5;K*{G`q zOjD6F%M5RxCxm#S70f0EM_4i0ODZs#EkgRWdE4_BWBA<;p82}{YJZLBp1LWI%bSZuzLCw=M^gCYX@6iL)0wiMgH$Ck9K&a9qoT`>J??sMsz|q zYSh)(@diBHHed4mOqD5@wzDXC^7vf&$apk=Y+fXreM?%q(I>N(CeiAXpom(%*)mv) za^S>fkI5^kFF0GU_Rl@=h7V5?8LQaDJ)Cd@aPAdyyHDr5CY$`xSKl=dqVRfwZ@{scW2E1*mdq z`8!uqxOfnJCYm-voiJ}yiM2XwrI(yVBc z*|N~Es;vu*bnu5l>01?4ca(Z=EX8kK2QCn_ypS4#?%F*)-{>}w<+s_YSN(E%$c&vJ z+Pp>mN_u-HpM05JqGf|NsOaai4TMxsRe&l%r)7AchT-!(ZJv&?7cxM*`$^T*~Wt7$$tBU-IjW%|6DKNs%XtLE}q2mTS8 z?2Fc$8haL+hR007!=gDI0Il0YH{t{fS8nO=D6Oa7#tkdN;HL0zdU^)8o z3^dJffS2PZ;Rw0)bYNlZSmK!Frz;dR~62P0!2fTj9tylsC+%mC0n)2ajuW=oz z6{i{_ijDOWv4DySC^_fz9jS(!(cGB^FdGY@hBDdr3u#Zf7V|44=^V(<@=-iH0UhYhf` zOWFd;GY(5h`pr+}+WI_A!5os?*y!=Ua;saEnF z%LXCJmCB)g-(0r`WI#Emw5d)-?zelX9og`AG1_0#JZ$j-xd9bzpyi@@&-WSn1n>Ko zOotD~Yg32Wh+Z1{x-h&P{m-ugu7CqB9TOQ~wA2ISX4pOBK&}egU)*`^3`seU{MJ{_ z4l)nY5SIU+t8tvyodWmOz28-bvAcM;Jp`=8c7Rafa!~YQa}sOcMhcQ1oqqGh4+-`~ zs;1@n?auRkM3vYLSJ9H*j6?uFD3fcB*_|4Sub><*#CFiw=``a$)izI~~LfWArjAJvnwKJJT^woD@y zr-79}>{kSovjonWa_<4X$e3NVW9xHiYpfOAbU%Tok3YObJ~ywl?62XbQxXw#3K92& z%b3c>By-I1wJG>gTiX%Ie|Cv}!=C<{GCT)ecD`mo`W}>e%0Eu})S@VU zO|;f5Yc$A7)hemuV)eX^LY;uoHQXqC*WfiSQ2}UAdn6PmhN0z(-^p5iDbibK|LJ=o zayQ9oW1~LWN=0YHu_Rfr zv`c1@Xh9mHP~snwx1-T>sYbXRv6F-F>~n53J~i&0p~4BA3sA)JQZWI7i}zV{5AmhW-Ktq44e zn{QoiQo+32Hrh-zxZe5pW0%sJ)WG`kySv}?w?jJfRoH66F0^+Xo+)HcoU~XhOZB6u3D9pnAgcC4n0A+ zfkcP6d2XqfZW|?~dRRO6Da{Il6}Q^l0yV2J5l#`glimYvbJi;=rJOD9?s_(`7rWmY zf=tXzG zwDS`1?A7z`Ji0q)uHe9?N^4gIX5#XCDz`P^V&`eolC57}F7W%DYY^?xVyu3>v_EL{ zPC`Q--}kY(Bm}DJo_dSqSYVs5BNvp{9%$U_I%?blQaEpz$SS5fBh*S0aI(pRneH3Hy*`>YC!}bS2+b@Rkf> zCJLOLp&vO}DKAXWWpjdP>zkDc*Y516a~2A~3!w>5fLSvt=HWOI<_JTq-AI!g3!z%` zA~vI>@x6lgxTx=Iel*Z18=9w;_A&IiPsWycz8`Ax~~woTk`9L9NXyka(0={$|4?J#FNvLq+#YFbF)<3o$^iv0L$_1_$8?h@o zeYr7htMw)3c+V%$^pn=N!8OucDiL6X4EEJ`!gGnPDx4LcPJfFrxc5F}_p_YS8k6xw zy|K4`aSi0ulRA28oGXY=WU=}aifd|PKt-6w4!MEPFV`!{H!wLaAOoKPB@f2iGc!Oy|dNw54QDgmmtS01t6r&&-~#FRFJ9M8Lp?~aF52R?42pnpvFZB?b> z=2wHF17qu*a7GT`pxTLwPf!a0~Cee_nq5-x{>gjy6j|f zF0>L~HB^A5O54zHPhi?PuJVpoXP;o&yS;YI$NSh`cJL;|iy5N17o<2N3fiXRa(R3r znqv4<^3H@ca0fn;2aK+V-9y*Ez(q#=D%Cy)sB^Tf^1a3ex1?m)_NgPwA$9Z;o)DxA z61daoSJiSQTbO^z(WFPWX~aaC5el1|2rWXv(bx1=UwU%O%iE^@V0!b>xc?Z0^~t+Y zgBJdYzIBLyR4DsUheD1f5ET=`+7~G^;zAr%8fH$M(L_Try(p2Qf zlfO_<^JwwD@%`3DYp9QE(Yb-sPfYO!X}}6(hQntf*;79t?k|OiH9LZ9^atwAsf(;@ z!DB4Ac3l@T6+j=KO=EtEzh}6&q!gh=bK{0*a(QZe_jD#RWnjZs`sfY*dfL|Xv8%pC!DVeKH9m=|tyU9pGS5)Rl%BvFJ<_O< zk)7k}a<|Q@p-?)ayno>$c!iC3t}fN{`xrMm9jAol8ZLJ+ZB@h!b!rr$Ovg{Ey-P=NIq?asMf=XgU_xlH9W`Ja>EUsS$0duf zf{$te;5}x<`zu6F+$BKt>s!H0O7dN|XTX)>z7=G&J+sDkL0hK;B)1@5-N%eto3-9u zgue@<*xeABf7TUE!BUPSxu)M*@%$58C`#-Q%=u3!D{!X>jy&`@-d1pVCFs%CGwWjkw5PSqf5q=8r z5j{6TtbESih0^wXGAus{y7+{%_GIm4E(T|@3XhL_(MlmZ7C)nyu%Z0Z2ICob@ysd! zc$k6q+Wpm|1{EKGbCGwze!kJs*o>r^h#g9P{a1!q&|25HcGo*z8Q|_eppg#w1*x$S zf*3=kB+GSCfIlkYSh#;U>P3S!JV*;=!Oz)yN|URHyH#*eId}2n_0I3L*zkq7C$ilM zE6;Opqq>W;Jca=?<1^1yD{g5^k>hJ!(}Kxe{5~)qWCwAq;GGHi)}LPs-KMiSVMDla zPs=wDG%tv8cIvVJw%b)@J_#3`v1Qb=ZO?xlqeTyJ1iP$+^+u8|y5v zT0J}wWDqMtG2u0b8&-zj!zbW|IM;-tnJVg&_G!Z5anVV`PJCW7oVe#>YP@DQn766T zXKBCtiDwS>fa8$59>gE66Z^MRTtjhF9_RkF)kEL@w(Z!gu}fXWojW}8{9PAQqm^8f zcjNBq&lP`Qz`ygY>X3V4NkNP~~lnh$dQzBO>mB}7Z@ReD3n82l4ExIc3>AXQyJQ z?VOs<7(7q9dq2DVmip7EfG4u2KfgVhEl8Rb#>vyl8<~{~i z*~UIyY^1}cjSn7kBqbjBm0h!}lr}J5i;!J}q8D_%rsaFryA&k6@=DO|c@1OMpmnc; z9|YdwT_@DR-qq%x>@_h^dO3gP+;rsouU?qQ_EUtJ{($@e%OT3ZEsvD*Bz@O-?6-95M z@JlIFSY#EgeX=^-*~L6sbnC&7AFi0Q1)``2iWzy+F0*&8_(NNWHA}RUW}+OwZ1{5_ zR0E|vn~6nhLNk||q!dCSvK~8KC1-NVX?+~Woz|+(D%bO^^+`T|Rz7|grZge%34On` z{2|;%*r)Et6aOKj&Yy5L<9TH+j=G2NwHu9JinjAsRcm-BQlGH;OOkchN`e+QtFjcf z`C9@M3+iOpP}Ooza|rj0j*~w25?lU3D?4ENgB~6tZK3!K+g|(K_quQ5T2uW@wJB- zGu)5LX24;w0!@xCy{6l4cMQLcE~MPHSd;JcIa&n+ZXIDd!MDQThuAA^3CVO1#6_2ePc^io`OErF`>vpNIiaNP;Fx(2vQNWu9lj8ylDF){JR8l z&aoMTc0x;f|Fs@b|E$O3l>PPC3=W)rM0WG99r1k6vHlg?IxDbu2r9!0aS>}W+%h!T z!e(wY#X}-qKBPQy*qUzuMbi_Ix2B>_*|sIfh{$QNU)*pU_GUUcd~J9oFvxPw$fiM}OU8(AJ~4o) zILk65QnBZ4sN!>)NA$Etyh%H3UIjmU24oO``ul>m@COjcmXZUL`Pt8QcV5|V|7_?s z79E_8472oEHvjYg)k@r3ot^P&Ov?7Agrk$QX2clx7P=C+ZucYo(({VU$6~Bs+M4vQ7MdT#1 zXJk2L7pl|vTFzw&s*c`Wy&+P%+9< zwZh<@cd-98g|{W8GNii@`2RBD^n(dQ%#yKe{vfJ!KWoq7d8j6IU?uaMdIz;ldVOZbeu{JRC}0j;2&XTG*)D3w7gLCFF)WIL8x#c%A9B`KM@dj3Kv! zze#~IH;Ogx!;WVceOt&C_gOf)+t@aPp{Zc*3#lw7*Q9OVOJz++VdJDgpmiA8z80&OILFy6NQ3k9Tf^Tl_$Zez-P;K4`6 z)ditFYwzeh*G>r#Z$3HkhW#x~FVOON;5t#qw|)kfrkktOfKMLyhA$K-iD4d8g2#`F zldF#}AnJ=)Q`?t6B2I&Pe!)dPpDd5z+qt)Kv)qzl_T%Bq*XQ?ymAv@|B)}JYh4X-? zB<0r8b;FT5>a1XIuz=SPDNY>2u^|+zAz8>>OHC$1SEZ4we!g9$OK)px0oe)jy^|lq ziNH)%4QGp2x!lnRzJ0poQxOdkS-I51Pmz3Ar009}AbXgQsh zB<+Pdd#s^csF9ALft4#T))x0In90Myq7hFOD~_2w(m^1s?H}C^DHWbfdBWnK0~p?U z2RelXe83+k9=JV4FY;ukm4jl2u#;W5uJ8ywK5qF=gxWFhy$NEt)ppL5sYe6%w12{k ziYOz)&GPGCz;ad$&?qoZ>HF0L+ZoN=oA4*_qHKv`-rB+2_QwU4F1vD5nzgS4-faun z2fh-ZcCp*+cIsQl$Hqu^|2p!3#5ULSs?hOg)9gCHRuN=IbVx)%)tj7eQm|jV{R-Il zO2acO;PXsZ&cWW=;#`&dBO&)7mYO|ptl-t#70heF4^P+>FZvuo-l7Irj@ACs=lUVr z>a!&@8w12Pu9vy`)Qu)=mtR@>swo3guigML0GCrwUd5N0bcRF2X-q!9#(~{*>kduM zLjQUjJYj@rt6kB+gynEKUA>2ieZ||enLt`@s*jvVtw}uQ@iVrQ9U0A>guFP?4Vwh;vvEloTIc<<&DN&OJfJIm_R@@xKE z(J$n|Y=Hmke%n}<9NwjHkzsQf}lqpo107`hv_{O(3lYepQk z0Q(4cdb8h%dVqSCo2|s*GBilp$uJ`|!*5REHV|xh^md?bf^0GS*n%Eq zrYn&?M_cViT)Ylc&ET>o@6)}_?--$~m0ReaeKjLx%!xw4seio6!P`pwSa;gtHVty& zOn7{=;;(X=ZgT45ZK9Nnt%_UPoUp}13sm+LWmK)ho#Hp=Z%xDc(ELR?v zrZQqAet)`WwL|yUiK%U58Q~C|M+KVoK|8KoIm`de@e6RpHStZC@+>ijIHzA>W^5&V zbszWV&nw&$&2H_l;?!r166fajTbAf5ExBiZg?32Iv4RCjR>ZiS^g_D6-+$7DNdMco zw0OI6)dB~@H#ij)@rG!IExRxwbi>Qw?QOzssR5S|31lnY(LNSlQHr5(LI=0vsA9{q zikxIBjQ75#NJ%<?b8D^QcsT<7 zWb3&=P3oD+O-Ie#9qdoe>a-1!gPJ!0f^_l}>rj_lChsa`k&v^cY}GLsjRwGmJc3m`keES zoYlafolmsl>(z1ApM`RUe|hW6Z!&H~*`Cfyx^nxHwSVms4+a|!ncD!ypYY|)IMF!r zu%OE&O!*|qjIr8?j2=I5XF1++4>8C(!U!A)&P9gYYh0ar<9_E(BEN*_!;62uNf(g1 z;8(DzlA6^CjZ)XjHSc&^b=>D%-f^OLZIO!ceOhczLeZJS^Luy41bW{tr*hTYq)_(q0NjQdg_=Zu>jB9!BcZqSzT$NQyE@HP)u#}j( z{v^xsM*rXovH89GrfD^+VJE`$A#Fk|nKV5gsNTY2QpP_+#F#B3Jfu|TTfJs1MCCsM zzLLeeMNs^y?#V8@`S4G!>9qhHLNJV`L%O(uZ5~AQD!e$nj-6x%@g@}wG&*zs4V;8H zcAM~JNU>^HIkRiOk7-Y;b7zwv6G^y--xC9*PpfY9q<=(epBhwf%Fd-93~KRKP|J6Acp`EGZAMqzPtp)0b{zjog!qW zY!3ttw-DS>fx1rOGADp*2rpHP?Odr(&ya}M293N~5#f-O#O5?teB6M5dHj^ft zD~M}^?%amWyx5s%nJZ_~3K!!Vw7X5iOtM$6$(FXGHRO}+MIpuwltYCwKj@J}|3VWl zHl`f1rimmhtAF)>$C{e+Gj`}huQ%JvYEGH9#(zhewo*o4>|rBY5ddGayAEZXdJ>f= z;q#nUd_@DoxA-Ju@7$_t>GU1rwo9%vx}C%~B66S-J(EG1F*%utI3dZc?jzc0*tSm_ z(>^Iby(3BbWQb99{=;B2ZI$(j0V1?KCIZ>+_1NwdTs4T~mn8q`8qfPEQtB z$qvcFk_}?*EONjxbUS$zS#!MVC$<6FU`k=AGCHCm{?{~1>Oi6@6))ghkn z+ihBYW{3AH(5?x(XDA{~QXd5;M?Y?p(4m? zKkdUNGx%9q{f=UON;%oAqMYRhAbglDXOuS`1?V$jKj=s}rFMwj(2eWlU#*!63y61d z@e5sls3Z&K!yLPOuJe~@OIGCYN(Ivov+X|Z4Q6V%OrN%`>D8JOr^76ks*sJ6kGF4( zF(*&YIHqyG-D5R|lcGk3vrE&1Y{0;)9ld`$6Ho^eeO}i@Z8Hy54coR`b9hRng6(lF!3+d{V9DU5&-J6dh`M z&O*x7V7%Xltaw!fS(HaHaVTUM)W^#hNI@aK_eVQ;4@DEu97zwn##cxIw&#m4$4Zrv8)T?EKsUx`_r2AyPdk?1QwVa zODf9|9Qy>d2dVd-FJd)djs1>(0CmWdG2DKVtOfv=kl^TI8>sAtBb~ZE0 z0PuqmAr`<6gmUXFw$T#0mc=sCYD6gu7PKpDKp7Pe@4--I1sB)52k7b!WuHU7^Jfes z8Hi{VBn=%bVV*{*jZ*b?~wi;tYT!aedZG~H22f}xb1+k_QOQz%9Qh4 zD9hltevh_j1CcxB_gBQVDTM#Zv$`K^FY~`0Y|wb|$JzgDdI#|MZ>ConfM^q^=#L=` z=wPNj{WkCH)Ahxv1?z2Yea0-g{WbX`yw&x6eh4;*-?sZN2n+8-5+I5KI!J8w0`j$tb8fTyj7q7!ob(DvMw(9!z%}XLrkB(;t(%mz+ zDS}!0K#n6fhzSDbOUV2b8SswK)2w~ETn{(~JsSIf9MV339j8u(>YPj6R}wkSU^ND) zgn+a8zk{vw`P-P+gy7@fIC8)Xf_#FYCtna91#%HUQO$>AKtv{ek}z(|EJ4xz4e`=C z*GtNnuKc;mc%?2po4(n|)!*Li3;72(1FROL*O>KPj=#R`IfwNHbsPKmP0hb`vM!(pdacfpHHa zUvW%Is&xTxb2yH$v;UtpuF(%Bkh}YC3R_H{Y5qS=E&{%DAj|};mo!dJQ$1)DZvFm#uvc3qJik`Lu#rXr? z+jGvF(dch%EZUAZhztm7IO+3Sp`MQa#={tl8bUqb+($??B6wRmF!?Vb&WfToT8o0Q z0NyCQB}w8%uZ$NTZtfBPmBh}p(?V`$*J%x$Q_!%0L5Si}DuS(H6i!uq2>X>Wcv&x@ zFSZ2PG`+;{QZ@B&TpStdaygpWk?i;X)6B>NLMt!QF0+t1)ByFMC%Meonr0tIF#uAb zo4)<^9MWAtVA>a0>Uhwvx2J2=Oho?+-|1!c3d`c(e~7-?Yf6yF=OsA=U9=cYo9TY` z_h|AXuOk|{{u&L`QJa6rX)EuPZQh~y6KkCeX1JKb%Ej{j!P?LDm@ruD?7B6rSrm1k z5xTC*xPIB3h1B{6!B;a|sz289;ytq4|LkFme+!BKyLj%?70ppfySPO)NDZ za4Uc0rKoz&|FU1bfS1d|Yu(O8t4*tz=CkN!;Vydq^EyOOobl~S`WlpX` z9nwrkMd7{;lGd_O1|}fTGj2R`<%;K@L|$h26ZXvdKxVNhpJZsuVY*0S9wXzqR9=BH zcm!ywhwc;@$$;Af^V#&oHwcr!O<*kV=+!^Q0x@}{Zp{M!1HPszbPB#7R*Cb43wQYZ z#iT20d@P&U{~BQBJ#0`HUxi0HR9Y1E0K>Dc7b4!0ZgyIDDr`>K|58(!~U@8cQnR30H3$({28iR4|(eFr$SnB^4-CaTj5TUOgQ zmIFbVKkLf(R9&2^pMw zIO0}0V7v#A%y_S%mng3)3@{5dV>X`s;PJ$3b{H=gJi0WsD9ryXc7nB25B_|1h%<-O zmVhHm_qt*tRTq96rDy}MQM1jD#laL29<1NN^ z(J?Ibz<=DP?#^X42Yphh)aG?6jyfYanvEcY@=iBgwng>(yAeK(H1=$7=aIVCJGg(8 z^0K-hzksrpT6@%T_=f@o0(}swF(Z?$TL{Knx;0Ynnw|pKkas&?%iP>{p2Q!)y~0Me z$DpDz_ms3D32!%@0A$aN(u?)enbpmp&w$VBFYH9v>NJiC@GV9^2GFT8WARr~#EMtu ztcMkcB_TrQ*!?~x7WjHIKZTR^@Jn8{KX;A3@fHvQ7Qh0r4PY78KK6@GWiM6D4Gsiw zywV+L>2-w<>`qhb8y3x0DnjU!&OfoyT?!=HaR*}?q%pYRj#_Oyp)l3Q9Az14!}5=( zd*+w0Ih7!k**95O#Q^Yf*AInL2XO;nW_!95*`zcl^5oDZCX|mAy(FwTm{lnIZFX|u zx_|!mU0Js+;Ko7t(7-&g)XH z%@UzK6KXFZtZk30rD&d^I4%F+9JA|*1o<@Klxj}>zGKLaxXUo1ts}l#)YfM`I^D5MBLYYZrswCA`1%o!CY)HjTqa_ys^3pBb~iv(&

    LPK#&X&*eR>gI?iHgnX2h~FX$99T8EV9@}E&p~OAVJtjn`YDZBY!B3$%69uZN#|b%R%um1@SPEJ= zZYrCwyE_U@HWtC7(+uBn1v)$Q#_z2~uJq zRS9Qs8=1Ei^87X{U9+B2)I>}e&*bOahfv7sqd#yaVJ|7q2UjGQzkw#&SuCac0)fvB zS?&($doF{jw~oS~*WM@o9IK2_c->9Ht8ob!>YN9E;*d#$LS6t{ggEUQfrDN~vt9wPEBN3cpdNU23|(Q2K=p)QgA% z_zeR;NJq9mJ9mtU_xTPK%HT~oJRVTyKfo4f?+zMi1Y~F`qK3*!XEq zERuX3Q=ahMMVWMrkdk=-Cie5SVeq*GZds0vL0^{uqT!)2>|mv%UFdp0unC-jPcZt; zI~yqs>5tiJF0RVuiN67Cg3emRofq3bY6jfEtD&sFkxMLpbO%jDJZ4WHCYA@Q-GN?h z`R#OVDmP))oolB2)o2_tg$w>|)wyBJsn|Sw$~6FQ2Y9$)QP_IuL7ZUHid^}boZf=9 z8h1CbWTIm?XlY(kuR*GF`fY)I8`-b~Of;L{7{(O@S8LuJ=Kc0;U$61?wN-K{!2qsk z$5S#TmxIFeu^5?_;ePUfs5{C%;^%8E3MBRujc6;d((+9B zH&DK#|2XIRXkV)CIW$tYf%+e8uyR4S!iJEtPG2&8460%|SrC&ek}ynxh5c;x*2dUT zQhGQKw*L7GA0gZUeATw*iIW6(wMY^WI6tUUp?nFAvnW<#3}LZ|)o683iz^1dIR@l* zLw(F+1*k<5GDM_qMuzEfJZ_%X^C&3oj3FywzukJEihn}}f{jDZ`h6lHzM3N0^VjZ~ z*WLT-omX<68LDW=oi76GN9WK7%sQO74)qmL?RYkVra`fhhwy!{i+rDDhg`i{mZtx#A%7|^{ z*JLyh0vobNak3Q2b%qU;teD!rc0{mQT8STs5k0liT()?x>g-U})k2|Etx;s@D5 zadL`|7&CvrQYf$)JqPqqy60e-BQH7E9*|#_mLdrUn6}>RdXYi0 zm$P#E$ikFW$htc|ekNovY-%6vEt13*j0Sz6Cc`=u)r5z#g$};>)%8BC4w#V=HpWR5ofvyG19LOCOmf(#kVYt$hnwe7~HWU)otXkOJgiq$!1^FOyL6KE2&F zYcr2sDT=f~@#>CL5Zq$@(pMx{?RSoefNy~X>Ax( z*c!<%=m-KB2xhv`t=IkbpgwFci}MDlY)kmN1f128#HoaBbyJKvAmRq!l%NVHfn_?V z3?VRM3Nu;$1QEJ0S^L+E;i{$&S)%hEl}ViE&1d-^@bf=Rh1L!gsH^_JPNlRH-OMBQ zIjmLsXfhTBkv41Q^-WISo785b#XdK5dKglR{s*gw#?o>UIB@oHGAh^pYZ(9w_B3q3 zT6KM$WML!TC`{z|4%EIC;htlRtnXb~^R7@fF|^64h2*vLHwSu~fpyfCE*y#%skc&% zCZ*C~1)84b8Y8Aw3N2q_t1Z6yZU7pQtksE<(E*i%EK3K{>=1Z-1IIl%+j*@B+Vnw< zApv<Xa3GoIHVJN8!Bq_inT6W=z9-t-eX?~_o5jc((hjY6Xe*8 z<~toepno&era~O}`-%S@zyI$W?B`FHRMmw+`u^U?mq4U0UAgX&$6@_`Aa&9jUVw6k z`u>N>DB5n6oIO~#e>0iFsud~4mhnlFda*J+dS#iO?aF7)EX&WFOfNnWlLH#aW#k=Y@Z!08TIRJN7!8FFJbB``lq_ zG?wr{$$vn;g8^XT@5{^SEd&1j2(K;jfqgo072t>CpcJzORrbnw-*B{U71m z>&M`{Gr>a@6k4Q=$H84bq{~}hu@&8{o5EFrd&hFwt zkD=kf(R>*6dbNVlXhFDW!4-v6?&2r@b}dYq6gV#jwE*b)Mej=oI=JQdDUFFwzmtvM z{NEayTuLxAoh7M%wVp6ErgQFbJUu3t)eDq63OH2V1>PhDU55g8*8(k1^Uk?T5=H}G zL#r)9edd4)2MPE0Rkegk+PDi3DBX?oXxF;&B0-hsO67?kP0;ufb{QI zw7~f_^Bj{jM+OBz;+eK$lBs6D+K!l!!OMF&Y1O-n_I|D4nJO1=Orjqq)a_<^VoPsP zVNOkG^C`{LP&;@AA7ZO|*@yNFdp}M1YH00tDA%$_i+ul^)HU|~%==$H;j}65jz4~hjkL-m-*(~G9R+%7J7?h z-`tNJNZoRKs!xhY9v#&_qw{0Dgmj@l;45niZ`$NxRYb!1OF9Yc)VRuTfz)LEmHl$? z;dIHL4>zKgTel&@Y9=)Q4f+FxfQ`G^3dj7cz_KS5+pSv@1u@@1UFz`dGu7LI<#{T* z*VMPRMJkG?F!v)~dNZsRD5$KH|7!P3*zS+ITSHZzuy!TP!YWx37hw~6*fEpRX>lad z0NI3JuM*gsV;BrPrN9gVrD%iV$+yNM9j`@wOtHh(J@Jne7%rXTt&bd+5}=eB1TNY# z)4bHi%xsmMKQcXd!r&3X*@%?%n;khD=hYuu;dRmfW3*5XF&-|Wb>d_}-5kCLJ+-FX zm=5Ve`qXTw*KS;~Bilhf9^2_Jt!e$SQi_M(#tKS688@~36i0(Hca%{qm{N4O_?#Yw zXXo~rWAAK?SI-!)Inv0UqP0cUlLx>&^puSKSd5EI>-Exqw?q7=xcSRaWGc`JQrh_Wp>5{c zxqC`u_XCV;-fMJz#`j9>EfmI#&*cSrA1i0s90&?TwR6O`7$`omVsF>Y-W?X0_6V+& zo5xe=%y+Ecz3n5AMqyWQ@|CJ@$w2g&*1$n^JU>@wm3d6I8slM4z-{KBvy_ad&9_Qs zX41uP^04oCW}0vt9j9Pd$O3I0BXNpzT3QXPD*o}BsIDlZD~w^xb5WQgDw~Jc?7niy zj3Aud*9) zy%MJ;oj&$BPUJVmP7vF=MxrcC&R(B^LeslfYb-x44pTyCPj*$*_G`nBU5hSdmv@=^ z4o$jh%PirIPEI>{RjdDl_0byq9lW|rrwG`IG>Ixn5HNP{=eH#V;JGN)*%H+Du=4`O zFx42HAc^fEG{9(y-FE3vI1UXLS32d)K9^M$yv94VTLKAtUfYpp{38~tH5Q0F1`lbN z(yelx`2w*}yix2@%b$3aYs%#urBetP_vmB(d5l=DFiMaCBhhw2$condKAw~EFapjx zX6^Gw9~iZ&krTTNGrOHl<%SR}_G=GUP!8%eGm_5LnCn8pr{m6=x1BOn$79m(kj{P}F+_5W5|2{K&B8b4?-c&X6fc9O@1h#I+eo z8RB?`wRJZ*ca6XO?M%0i&vu_e`%eeWUDqi4-d&GNA}Iqj;i03U9?JTcp9gt3C?{h1 zjE8MyTe#k+aq92%Qn2u80@^X5lB>wi2E`Q^9%(phnSEMNPqZn*d)%j(IMx9b#?u2ml(rpMXT+4pd8+Fa;iN1; z$KWZWs%q|rl7_DFvl$H^YlpxwRm$O7Z3z)Gz@#Dkj@Xk zLhu^BX(41?7igWfN#QC<&?qCe4k;bh7BNS2`$H;(4y!<_W;)R!1%ksRQRh9|F@l;` zm%z4hq38X|+Sap2>BLw01BT}q{F3E@jD=2E3QcgWOF4VDg^Kx3m0i1tPMKs`jukyM z%_>nSnqpRSqc}>da6{%d9VuD#7WPbQ;ukF_MFVZig;P}(%4d2m~& z%V9Z-oigG!&8Z+^F#n#@=tNLoti+V!mTAj_?p}fpUl83Q;+^`EDO% zzYlp_^>dP*jCNr?)`3*gg(yeb@HdJxQ;Zx-KPiT~&}`EPkxMKcK7gv2Pb)n?QdVXg zj*(pLvJJ0$_@_V-Ys8kQ?I^~-RSzf>%OG1}CFCe;QK~JtOHI-A0_7FotL3Mhq^(iH zA)Y<2CbPv>y)Q^V(`cMuGl)m|S+=EB!lK;vSPY%~$D9331^K_Ix6r5DypzCm+p}XUCY7Bt z{uX=6aO-M(L?SBYENAO`l7QSv%GajnuLhSL)dFBW3>hhqxT&hix5{VbEb5}ds*EyG zPAe7K&W&|?3<~Fs>ZomQvOvdn!q$JzS4A!a+5E*Z_caLpP)v^5+gTR>;6I3RuQgn~ z2D+NOkpyTd;3Z@IZnZAv#E7bg6Uk-L@qvS}X|LKVqvrhwMs*imx{Yuhq_5-L=|vSs zA?)ocO$w177c}3a#0H8{dR6MfD=DmO@XHC}_L;aub5W)4fnATM;X9ka&S2cBeolc~ z;2OcSq{jkTS3$;?FxB(=rT*@;q%XiYSA{K^$@}jGC28oJ0=mAlv?y$9YEp%4Qp+7zl2@+{duHO| zEEBe72X?}4b7pTvOW_s-MlfyZ>mqdNI)_zHcD7%fH{w=kZm!&vV9y=?&fXp7<6xtZ zBnL*RVte26#s)k~euPtIsJU=r+yy+@WXp6Vvco?5;Ps5dlND1*{&M&Dv`B_=# zzX#R4W24Q__6a=O`>|Y2)NAMpPyy87>tn~n9X6#yP~rQ%!%d7E)4vq*7~fKG*5~a$ z{+~spLrm3gD&~`GSEVy^dFZGNnG(DHJvJ?P5oUDJPu#ukvkh-eOla+kdfH2_k+mBB z8WtkYy>H!+*z-6&r0RY_vuKL5DLe-LT_@W0lQ6v9Q8{S!qoKm|Qj3&u-hQ))5_a58 zk-2&+jkyB2F67;(V4jaopW69@lIh?`6PWJ|DMyN`yxiU4MlY)dF%(sFw#(Bw%3GE* zLRq=vd_x_py}rx3g?EIM-BwLB$S(JYMt*SYW-R|M#YvX5=4;cp#(GHe|JDQbAA9jq zSO5`{UbB3kIEg+`-Ss;$H)oo$VR`Yf5>7>_xP0;9o1qb(@sfczkr=8sLynz7Tu0if zvvsFuX3Cn%+DY8hv8e}f*ly62e4LJedj>?Gw&YBBx#G=@dCYyb?Nx=;x8NjkNMW<~ zno8b6rK^oq>2unRs53+5c@pqxbN|TUCbOmeAA1 zAKr@ed>wi!d(8GFnbTE;5$Dv&ocD4YrflR)tKTVg@1L1Y9pU8&18UkmxvTj7DQuq>p6dzx z#z;z^r{6r58XEOa7cQ-j0yg;I{*wbnSFbKCQo$y!+c zdw|T#LX0I?Cd6QWDJULrgY%Sp+yo-{ZMQ@eGSFvxY;*+;zo+;Lph=R7=QYDt4e_dM z59UX&btKXzj2vwxC#D~PZ0qdFF;L3{QS7MbY5TpFK_E z;>SFdxzAs^O)tUOF86FFNSr;o4m{DrlzE`LZG`szo4C)8dln zhr~$c75e_Xw{zpC%?6{ZeEHT zt(L_S#T)fkk4|x)agDLJvY+SCMp6U%<9I`6?)g7G_M~+A$3Y{%>j|T>;sX#ZANywt zU66u;NbE~fOf9%$N>1~|yPC4#!%@W_E+|jDEFaHYy!fcO@oJayP=Xfk<<_`Iuc+#1 zb~Cco8OD{a8p&d*ec3?P_S@NX^M1q#`dl{Paj>n1HP{qm*7@wN3NA_V4e6;naSb;$KrIbk7l zf8|2tS8Sb!-Da8CIU%tgDbsa#lv(34r7 z#d`bN+wFHk!wIEA4_8g`ENll04qm`Y7sM;%GoPQh)+(z$m$`9tQSO~fOG@r5+K|kN z;R&~-aOBADmtl0W*sgzteso$~XPR$B@okO1@x-k+_#j3(mPa)<0^}xTyJ|@3Fk{we zCNy&8GWoX1E7I$`%8mTomvH+Geci56a;c>sDu_9-8w0u{Y%Xn5c{+s?Mm+X&n366r zUbh|p^;53q+>-9Atl{jl;?TIeJ|FX$PrY|P{xf8V&CHbZ#3o(|4-fF?k=dK#?zE&& z7rmmrJlMx!X58wgbDDNXf5Bz4_uhL|jF*_4`xzFiON(0(+hs?(78BlH&?i<9W&=`e z+E;aVU&67{zFRF3(t~|(>=~4Sq8BKN!A??5;{`+eD{il$+lDvK-)gKD^0~=NsxBFM zs27eH@CiTsOzxe8w@T`J5yM-J;8~@Anl6G@3PU~9t@jrJHMie4PTAK!XqeZ@TU_+l z{$d*9FX8l;Ofnmet=~xNQ{^KJtRUxgXz-?W!VS;a2Z}!o#-avN-$&GCY^G)}<{G{) z8kJPjB^2bupH3scdI)_v&}N}6Kx@cz56544L^R338E;*iZ+|TSNx`=-GUM!&$~W(e zK}s5+(^4>^L{{+r@$1t_XylOt<591*DO#lTzFUO}QKlQ8bw7Z+oMy}}VD1`QStsU- z?gaB^n-_ri zUQYe?#s17GkrbniuoYH;dvY&Xrh3G_S4+XVbg;f)JvMu<8n1>Z`E_4(^}QpJ))kVX{Qt21nLRqqv%*joygpsCJeHT-_?3ej$vPE-)hs@GHrX$( zM}yxM)ChlX=u^W7MTrp~Dc#QzOaWn!je>0T;(Fw}Z)Clx#o^bWhkxHHW=q&Rm&ch1 zjqpVGT--CgIiOL>JX6x@=2(LK_LSLG_2R+Tl6MlkRAeSKf2dbH3URFUD--K~i&;-X z#SihtMIWg6UU7m@Woefvy7+!azAfmxKs{PH(^AX^21;1N>OkdguU}P!stPL+HPM`9 zWZc(rP*`o`8r0*{Bme5oN?<$!>Cg#l?)IShVJ^(t3umaln-bgWbh7yJSnm(nlv*z! z*Bsz*Qexhb0A|0{yPEWb$-%OhqbW}%Mf*G|waeSi?KIPIU(zG~$`8OWFU<|{a%=RaIJ7y~NQgNiO9VG;HRi4JlhIbJ8mir0c1y;jXX(@)Q5qLy12yM-cgr?eX} zK72X+7T}y0Fb50_5m5}tG}>JU3kj9XdMQ? zl0urbewBWm^!aXINj{RzvoH9mTbra@)JGOrOy6|;=kLH65%PK^Gf#JjIC_ocr~kPt z+5W>bsfoYn5@JSS?dM(>#qoWjo>U$FB(Wy{7?qvm!{pPT5475C5Niu179w1X_}V7j z?9y~BOwe@*AC!9pgxJ-Jq_8E7_MymnYFgCT})DUSItX!NULe;J#^uBgxxX`la{fH)hBd##|4vmpSXW)r!z+sjF|kVoOey zXOOeo_2HG8$8jfy>3Z)%R`sq5GT5K(W-a5GIfs~@?H!PR_C&e3_{{E&c=EFIMV7SZ zG!vZ{8y(iLm=)gC_aN{JR;=Od42m363Ubw?G~AmfN__5H$q4&i5w-MJPSnGiE8j&< z`zljXo&>QdaPC;xPmE&*qHdy6ICHXmb*!@DGc;<9$aeJmj)`pvb4F`1;SER!4n%e% zE2+j$uq6$Z7F#)&0zjDlxI`3T+V3X2qE%AaNRunmB20|+oDwfg@Tn7mE*J#b7Zhej zaC@CNptbVm>XWF39 zAqJ&|O=yMEZmG|=4lNqwF@9%61w9lygx5W=%%C;l+Ra=zA};uPlg~v5;;N}vlvy;M zobIY_T(@^HF3_LQy~?iKW}!Hn2IYe57-PKkFEPc1Jp5JOgE&lawEJ=#a@sg}ZHDjm z{)MKqO?z8lpI&x|>pL|mA7Pfi*FJt<0#@H%rDwCw){<9t6jbP6+!v$aV4&RTIh~b6 zT%;bKOSgIjtKmvBKJE??~=}5jQKTYapQ9P$T7{U$nTNjR>th>&7&_@|$sn?8emuOoH`wEC0 z5%Zgzo6uS^A~Di5-2q5UqP5dSE>2Is`KsSj<=hLDz^!h>jhHz*0p7 zlVWgOEZ#zmIp+eI!!)kwhFR;pBpL|cOPO)mmU3={%|p}H7Gjm1O*jXzpq;AfVD(de zmQ!bk63Syv>ifz)k>fUPiWHUeZtrlDlV(oj!+8kp;nVeIKSr$eGwDoToRI4fM|43o zp0pdwp93F=?TN4fN@h<>>-z92;%PY#OVy<}bGG||CSrgi*ZY+-eTh1b4jBt?(F0h| zLd*Od`D{X)i3TDsGWr}o%5;gcxz*!-~zj;Z)+%h+~mcftqATFgMI`7C8G}4CM=H9nQP=_JdPl`^JSkFzi@_37d$V1S5=I!4T5}gi8KrP?gTFvVD_7)&#zwB8 z@9Hey%~fsm?mpe{pomR$JkjSM(ksRxmBYG&P*QZ;mlkSaf_U05>Fld+1U0p>nG^M0 zw6!OlwF%YIfL!uCtJG~2c%t*W^?C5M997KQCL?l}y!#J*(E9ykO}VHQKEyuYej2&( zw$WLz`#qD=#M3vvqt&Z%&!@~H+~i_j1-dF)e?v&xlAK!B-xX9oRVuo>1U%l)tbMWOZGaVUunA}o_ZS6V#eF2G%A_s4^ zn_fIVZRCBNN8nPcauF-ql_!@kMOg#>T|zHOT`Vl?sD;Z>ldLc+(V5z(G6n+v>P?b9 zq);bN7f0_u5JdlhLzZ&vF~9-;1QsYP_~o)snH+W^<*Xdr*QcVJU_Ln365v{`bK+XU z=!y}A*|Zf{sGB;=F;L0{5OnU|U0%72>91LeA!Y+LS~`O#Mf)ylruYi#KJMQvj(?9B zIhw;|6(6+<=0mi8@%WLvIbi%P ztVkJ*TorsdCmPXLUX&V=Xl$t;@xK!{{d-2htwqcPn<0Hq~d^bYP zc;ByLI<8C4VW+5jt9ru127yy;!d=pFNB!QCR>uMl2=~ZG$YB0*ctYQWGaUyFps`Vh z^r){rFSdt^kAG=FN3q@Pi{*aer}RmBB7a4}^;J&t@|lWJbqMCVv1~1b<`RM8M^wq+ zIbSdKa!%3I&9`K3f7FW9eXa(^kESM{WSX)_Bj>Bf-E7q$w}leE+n<;m&5kRVa%p8b zb)!$CtSsZNr`^XUWG(zvR;r6lByVMl53||Yg*_Ew2~h?BpC;gNASvPBgI`sz6E*}Z5fP6elI@wXRyTc+Zso`n6ytF ztHpc2BuKt2%@B|4EWWkY)aayrqiH$n3&&t-4e_4;9@>N)%~kmzs9pU@tAu%`=z-_F zQ|A-v_)1@>d82tCB-42=AjVXd?s?E*yMCr}&X&p8`)ogZ|C0nu_KrF!#vH8!`=n~x4Mh#vpBStmn6jw%WHh0_(z#S6@*c?iJo#$ zY=-0dtXiiovCqbj3iez)cuDzUOkGco{U~zu%6vv^s!TnA;RHPh&aJUr4^hRlMu=;U03fjIFUK#9p_9*UzTs)R2Ok$-erDj%R5p4Mf2dBjBffH?yK>rS{&l{P zQ)xD~pPZoa!j;T*W zxJ{(rlRugyy1}rc=JUEM4i}Cug~+uBG? zAzwILH3`A32N8oiAmyj}> zCRcnxi!S~Bt4lweT|sowgLNCU@}LKH>GaAfxgZsrz`Wfb>7n#iW8>;>fBX8AzES9J(kkv6!c=YzVDewAkjtF{?*67o^^8+gh?dHn&liD zEg4;Z5Q=@k7q087o$Y*1yHWqUm_vruUE}uZmmc~O2XCD-gLTfh~<`w1eM?ZQJ3j&bRAG|J^QS4F#=Eu-`SOP(*c!$nlym&r;1W!~DpH z*HT)%p~{}x9Zw;rE^>WE9#zvy82;FjP0mb`uFXAD%Mi8dMk!?xN7d&~+l^uB5>_89 zUxTII(rRQl!aST|Rjc2d_Js?{rKjF2aB{_zzr*KL8qi7dM}18csIRG4ndRn-YyfEB z3kOdav!N}|nPSq(nMezfWiv&OY+pO1?1Q?$tuA6_`IRLe@>h5C33nI;FBj;UM68@! z)#pu3Q?~YhTKFc}*7U>jvy8`|-bZ{<#?C;4ICk&L3VLk4ks;fytOk8c@wkbmbveD4 zHeTDLDif@pw-rFV+=0jRONO_>5XLvS(LBW3eb)xO?8i)6Ba`hrxn#;+qA}@Lol5wR ztsi5070Yed-ZtrJA7J7>I%zdtou3%iZ3L^d@=SLIsafSV(czt zDY`=7=I|X=@b`q7cez$k7rqBTbs6POUf!nw6y{TPlD(6(lO=O6FH-R*W+IpPdGWqb zU}%MorsF6gIsf4v&qAz{`&~yijiC3b*Wk4`yH5qT+>FW^>?VeOnNFq zKIJR$FAM9=dXE~enZl%aPmtvCo9~*VLe_(=4lt}o*q?2`CRBH>ISs03xl&2>0p(gc z8}-WWeekOnU|aICa0s$sv7Z@$UTt-tglzb3NVzOJ$Zf1>{M7Tv)@a5}OYL@qJ0efI zqoqS^$Gnuob5*|9-A&M)eA$vCJ?3Cnc<5D-Lt(ooJEL9wWKx)TqpK3&bb;Fq1Hy3W z6L#%FtOj-|oO|NJ>6>EHD6*!$V~?f63C`uPgN{oh2b5>C@Ntw^?j;YKXT?K_I`k4- ztPrZkfSNMlreJ(c7s+a?6xZN^+Plzrk47D^ijU_sA>1kHuu=Am%zVZ$6Pg#4m zdb^EHi|<=rWmfONmy+%_|9GGx;Ubo))Mc=&FCksmTT0aQhbY>CqcoTGEe)e}p;vif z1?m>s{3I#$L4!LgdAea2hC1J=<;8&YUpcyqmR>iwT5CC9e|XK{J)cEAP|eG|wA=m_ zzBD1GK74$;yB@sx?Fs7j4#6ulb^Y#K1LIJj;g_J({z->YV(%Div4VR2sww>kge*&0 znhg2QOZ<3vZ!aWaIGg)!dt16ef|uZUucCdYlA5Zc2l|OBa-)wD9APz(ceq<9F|3j- zY3Apc;WFzZdM(B8n|eV^UvQvRTDFzRC5u-@k3MIU&!%BE*L;u2u;(hMCX{peX8@Zc z-My30*ISx97xHnfFOE<<;5GFv;g@=dbdS%?hQICjZ}re41BV^lmldbYPZ+fE%cS`g~eT% zQa;FGig8EWa|RkyUvlyn%I)7R)en99!OkKUo-d_6CHLuWDMtT`x@V`2UiZKiPw9Qf zfB?8%7VXt2T8-QR!MwM>oS6lHU>xUv7`LhZyD4Euq*VE!dEKi{<&Tr?9~z~d4)kzV z3DJo&|Fw<&8D@pk^V}~$(r&fMl#kPG=t@5~pd-9;4%gZ?>7+iq#Zmtq413NiU3usc z$Az>9uB~=1+o2a8hQ}VDqS%C3cGP#=sKy0bvk@Iu*Ww37D`Sps5C$3 z`F~JG{aUd9^;xSoUAim*4cX3Mf<>Nf)Iy2M=R3DNewl$sRPo+nbpf)$pF)qdjn_833C0YK*V5CMS?fBh9#reFj2%JuU;e#CHDB z`usdjdf(vA9-o8vlOWwifH2QS_wo+X$g}vN(hcIpqz=<=BG)07d z{Tt}J-#9x4usH^^+@|(bWyKllF1$GG+a%&U2vDe3|KYRwrk(7G^@`Eh{!_g^3I`&axoiRt#W*wo+GTAzoh2>t@9 z{wdGA_YR$aJN$p~c@EB-Q(Z)NgraM39e-L2S!I0M9-==Ip6BR%`c0#{8{%_pn`zKls+sH{@ zUczv4UO8|9(3j`8TLS8o5{wCI+j2sbJxI&375Wmo*-^e=y+2uX4^^S_;JHPImFvAwB^_uKuQ#eLUL13O zv(&=b`)y@j{Rj`{{uZifoN-4Ty9G1+#rLo}ayRB9weIYt^q)`go#6|qF{9X#_;7Bv zW(4Vdn}GkaTi^J!0n}^l64QMhz5ts1F-r+)ROzy{vv=2r&-D`lRa+pemT|q^zYnm4 zP{D4ivKx%E05nSPb)HD6%vsSXK)b%#?4ea@lv;bvC9@?S^V%8WgRKnx>IV1}n?99s z;MjFO0a+iU+rf{)%C1h7+JOz`L@U8KG+5?IUCtH@ES@sFYpb6_iGE^Ac7Dc)!!3Fn zi1JLFE)6|2nomHHv(SAB)Z=5AWbmr9P4qPV_pkbWO6gmDOjGn?ZcSj=h*3I`I5~2? z%)@+(tR7F9n6_61dIb0cSO7{D-p^7Bc#BwObL3AxgYz9Y;~bba2Br}w(3DsV`~JVo z;gmecMiVccNL?g61lJH1rW;J_Uga!mEtbWydI5T@MX`-Zmmy0>00&@#v&>P#bgniJ z|ZeVEVcJGf$Ou2q?3}z9$&{9hFe*tuO(vY z%}dus_o0;@AEkBUc}ntL!FKl@f_jY^KJ5J|A+We|-Z8=N(bHsKr8|Mhbr%!BH*5@G z-o>s8cl-MQT$_34ev+{TifR7{_Qm;kVFg1s!r~?H07NruTb{paSvs-xl-F=xM|(^Q zYe>)UkEk~sk#9NX$?ONBwFUAzdp8+NvmLHTmbD3Y;GWvTC=imnDTx}n%s&7Ws2!xY z)!--G7hq-4X%$J2l1^5hGuz>V7#ikOstrRn!=w-ZJcVSzx@qzbTN(CMc`Fh)RnDKz z=ZSNKg?z;a@vK^x&q%V&i6Tp!H{uc%N~5;8EWj1w4qq3*Jcrh zhw*+|ATEh}d20UomJp>ugyutVpTd@Q)YcAz)aFSF3l}DO<3F z%O99!fotMJ%sFF!%dO+V^gibb?`At+fF;yaEMAE>Qy-SwpVzxZ5+WaKTA$>UD-2hO ztw{Eg)bCF69R}LkJ@Yy#iRQ&WjsLDu<|KGqtp+p<@ZD=sc~GR*?vUbf(5@mB%O(G` zvS@8@x<|yZulFZQDeJgJk`j8#I^z82W#D9h)G;ui-%eg-w;_Gr;U#&QN(# ze+Oc;EX~|IDMBMJiUJ!*6_?-m$h+=`8+(w^6bor%cd33`krzHHzfWnUp8;QDRGbsU z>f4mHkfbCwrMm^nvlD{cI@n$}q=JkS-!r3 zu)EcMAB7wxf<5*F*e%AQk%!bWUL9alL|tE(H920gg$D%COcGarmm0R*-RJz(k|M^H zQ2+OJhY3zhX3FS|k`o8%wFt^cJ)8)l3bQ03uZl%X98;Q~7ap!Z7rEHm+OWHjz#;6( z4%E!?@BoqBAIIDCGTsCKPBwRA%@4ZiH(CHd3KhU|J-z>CWqdilfur;EL4y;mH8 z1d69n+Dq_;k{B~39^aBRSyV7)h68v$qXtcke{4#FtYVEqj1cC|*ww+Ys61f%7c+)e z_mC4n#v3e@Xys0szns}w7=Wm&wHxD2@5k0Qe!_hCyVb0>4Q zC{DKZm8kq1bG67AmX-NkhlkX6j8p(hgDYqF23U89eL0zP@ndZQj#Zk8Cq+=(bM;ej<%lCj`&|P-{89T>evA z+5MvkV)~GBJB24Oo52Hk_Z_af5q8$SW#{8wDD)usuIr+5w}tzuw+n`jIQc#K{L=+* zArI4*7{&1qDs#9bExU~w|H0m&XgSLn)hO>KMabeIo8ds`*?C|hj!TdHZ2(CK&}k)t zkD5O;&FYjIv##*j8W|Jp%yms?TdoiIkYTXI)h#zt@qlD{M0vPN;b{VbBW(Mju@2-` zjA6~f?m8`oOTTi?gCGg`y5_ib0Au$H-#eukA4?7JNHGH_QrpztNW*Ou%In6z6*_!0 ze59IrIL$ofw5pgZa?%-jBVbi*K%g?sn^6E;{^7{c3VnR2nP5Mx<%-Yf&EJ+X)nrj* z-z>Z|yXSvG9)2`uG&3*}@VaG2G1CT2B$$9&m1NKTod%Vs9UD!^nRHX)Me^sQ?;;y} zyWFeBa^dA$_^$th*l6J9%Bu?-eq!#`l`DQgVJR=C3;#|+@?qun`|~4!VF6L~k_C~A zrB{)@-2N~iQNc_)^=;!k~NzV^}pm<#5G?J@E3{@5W8asDM>bPFV`mAAJD;F_kJq1vE4 zp4kkWz$R7dAee+n#~Dp+%nTTnz!n*mSBc^9way6iQZ<-F>;x}0)+2~R@MR)DZhg6) zPILtMR?HhKlUG)|J>kUVejN2Z2LeaVVNs5APFhd0vE$eb3ZD+jnGT5{1}z#{q7)#L zV$m$K+jBa80u#5dQ0Es8PAx~Uom!NK^x(HHKCuw7fQ8TEyv5YM8}g5f;0gdneKP+~ z3+Se{-{>+JR$tGH2DF;qkv%p; zt+HAF@X7hni{W!>d#|lfCkXTcCPqg3p4igXKf&iRhvvRAKQZUuKfr3;{FCCE{U`wY z%FQvpvJ{;FWNF!R=osdZnm)hEK7QQqFV<&f^E6_Mu(~iAtz?E&Ro~*qw!n!bj-iHw z`R$YB;FXJwwD3iG%~%9#aZKE=g`R*TO+qM=2J-ZDPru=1U)*LS2YQ9B2VXrIfhKy= zC&tR*YmyNlQZB+5I=qg6lSr9}P0t8j*oK?9GJO_Dfjd7qG_8R7A+~WcY0-OLQhb^Q z0Tzt|TCNar9+{mgQo?^FZ3Xc>`U80YNVd6d7Xfwd8zhCWP6K#2tQHPSTmwdV zjLW+CB`)!JF~-LkUT39vcL&=|zAy41j#rnf5IZf+B_U;wcKK znd%5I4NI>bOMnwIc;QoNU;;4+K`M@bQx}zS7gluhS>cWE&jlPg8MWx?w+c>p0~zZ; zTqLi;r-VVLoy8rH4UClyN4F$S4@Ljy5^kRFI{r+&ZJyiV1MSG|Z0O)sBoaSH&EzBJP#xBghqc6DuWr}Ho z5M}s0!f*0D1PgB|_FG%#MvoNA2m(M+3~YKCP6zB_#u)ECs?^i1~KzX#(!D{}`v@?smiPelycROcnn& zayRv`ndsEl0*B6Sf6M=TcRLve;5zsLP@!4xAV?{1xs6@|Xlyeq%f<2I?z=yj8PJ!< zky(`2iPgCEF>%_}<368PLBv*&FSH*_{4vH)U%@FV(?jSewGtgQ)iLr0oY)xwUNi8c zZ2d|Gai)V8*(n$}sUEaQ^#yl{iWht2uTV zNSXj^(dT?x^%#BC8Tt(#@Mwa5=s^Cz&3je!IPty&`XW0AgNB*SZp3eplW=r)2dKl_ z7B)z$K37C|*-8n03dG=@h@Xda9>a&FxhUQQLY3%~d1>cUhriB!k}JJrN^PM6gYc#? zj5Bmr;y8w8EPjzUf`cxXN23Q2ZC(wL7g)fk5k`pfyNvQE+SZXAyM6QnN+m+!^~hk- z{oJ8z!j|R@r#{>?`kF;hj7P|zw?nCx!UjP`dRfGxGXLs68(^si+j#-_K7QSAFpeuf z`FEgOPt3Tq>VpH5*2W0%&3ZH1Uo-$!${ztDE@70F2_!s~l)`S$I+99KNZ)2Ck9@BUoN`>)90byK1x z(uNe$P7J`FfxWFCz)FV^R=q5dX{30w72u5Mc4|Y0zYVBO0jp{=3XHlyz=ts0s+L|K ze&)4XlNIM0yi1vYiHuA47s?~Z&J#oha7RB8$=o$Eayp41VMWH|XM(_%Tb$0=2O6T6>;<)c#{bMUxJ^a7{E6eTKQinp7@WX;?qT7f900l zxVrwiryn)J?{{BBmRQLo%J|aY%;w#H7?71-*nl%$MzLmHf|eGy@_<~t%&JpE*)q~gAj0MmKQ2dQPDg!M6S%CfaWApP zZAIKX(ygX3S*J5z`M_=yg|z@Pu;V>uTFMI8B9L^NF6xE_9^AW5msuMBD{Wd#k=_O& zEprfQi*$_cfHM4fw%MTYIuB@@e`M$Zyx^CmwqvN3&vC)~eW*_M!ye`0B8EJi05C=g zTE!b$TlkM`KRWluTTz+6NtVshR@DH6<4>mqyF!=Wwe-O0>gZ@hJL{IS-W6o?jh}-|?{2AsWkV8lO z?mG+`Ejm_ZSwKj~KtQ}!py}?L^9XS)Z^4VRpZeBJn`ff67;=Vu`6GLBb_T|!@-H3z z1K6*pkMqwd82r@>U|Y(A&1P`^J^b1zaDx=jHJ#?4X>-bFL*FUpTH@y(`U*hVmSr*fR8e*tfb9SXd?0y>^_aPDdPS&C;6Jk` z&~m{BWkwfXR@7v*sqZcn6qqo&`V1gNMMS{nEJt$0F~D8hZgl=WDJdE;R^l<-0{Ffh z5(}9Ajk^~(sQBcIIo6I9L&q3_JB-JF|K&d{Q>wDG5*YFCEXVNHtC9e z$bvM;kLt`9heJw$acOL}0oYS9sAA`{4g;b6cYqJ{kI^>SYSYE2^=$DI_yWCDDnR1rS$sVxIVBUSO-6hEYXZ9Bwt4P~ng> zl^d2*s~YDhcU^bh7{s zMyFqcd%ir|bYJp>b_PNOoT$Ptg<_d?aRSb3UGN3fF7N29nX5nn`(MM$)ELKZE_I@y zk$c{n%auCQH4qOHL$c&HZ0e&WF7u3hF>c?Fv~lsI!$Ka8JsjBb1OLjBV)lKBLfaV# zRcJa~I!tMPZ?$=fYV)XdU6g4*IcI+{vHk;?irXS%d+{G=p$2p&r;sPXvH{+y8kd9MXjeGPSvZarrFb?M~nTUnf7y0jkmrSzwI-6}!8_3i#0a1?`RV|C!B8B)evR5I;OzDv;}^Bz{`r zur|C^U}B_ewxZ_b&;*y!cSv{EjTB$KpoNvNJ#|?{W$fy?2-KyssCB+bP=7T)prc@C zwu7)63Pg{``J^x3Y;`qTPa1tX9_%dmhO&OE;6Bf_gD~7GsdbSUeoAeh)#Bf9V)`9u z=7jqIIt%dDN|bN+y`J`(dQA4Z;K3NSm*{2gA){k9#f_-LiI;l=Iw;GC30#&6PaEYoMXKr z_vP3h&@hX;?{5AR+ID~VNCfKr1!OR_$P<0%xriS`- z4bTEkuOKoiQ}Qv-2m9CWM^1RsIuH)z&z`WQWqvSl4!*o+tQ>z{)?_<|_um6dyXV&O zU+oJ{k6kF{qXxBobsr7)0wH&qj)FRhSS;cvY7dvmSvR2wFVGo>*@vwwA9Pdr~4KMZELX{Rd~Xlb0Ixe=0lqQjo?@WaB0+C73FJ#>SX)0o>0?!xBd zE*^h9jktKcM&fOO=4kY%2#h!TqGnxR#6QLg&+SDAFMLTkiLm7@K6TZC+p09pbTr`# zQL|~-%kfMcFqZsRKIJ2YG|=jK3X-5og6XC8AW}{Qx*53Dr&Dt|f)+6JQFKNSX7ApS z6&5#LM7n$o2E23oU<#t z3%kp@Jz-dn?AT2d{EK7d)@+n#I6m_gAVD{HLdv;;HK@INRu)o~BHpEOx$`Uf>XK>P zQ|w)$F{Yw6za*%illd~Fd@|40S=7R%!A*?uys`LU*T*I-w8*pL(Hn9iIdI=HzE3`a z;jKnQ;i1`NPw4}Wy*XNYtjbpI>Kenx<(?yB_f{!+JdTgPOj7)ip(r1v(V#27tkWQK zwoU5q%0rsvl#fTLh|^{+VipN?JrdSyp02|#JmxuO?K&;f;Q8OY0*GOmkoKljIxRs)$0Ju=I~zMR;DD5jH3?*el3 z#2r)PUNy4wn4_MwSa%MU@in{`XYA5L$+h{cQiQ;WJl!6Th88%ve|_~T&lS(N_WkcR zlVA9{b8i~tj+}VTu>M@G0Hcc5gDbt!RnQD^z=BG!6%C$$Up@$$)jH!ndsdlae_?jb zh0eETO~PoiH$N(dYihss96y7U<;c}G?zz_AhWqYM`oj?A-W3e!Zs92^7QPAr!#2S* z8(sJ)3!twcrvf}nX_nh5 zp2=2^rb&D5|Y@G_XM-fiN2~-n%`XZm?#}JsgWME z9}__$-Z0=@pO*TlDM%G#%!Op8TuxG)NSz?Rv56`#ns^1=n}638y|XdPb09FkFI8hLr0~O*eVWVoUR4pYERf}u zr4TkPQO?pV?#&BS#(WLVHvXc+W_p+PpA79Y8mO5!1KbJ7+TKt=`7_mcJ{7Y5O^(uX z?FtKB{2FthrhPTfk&GPZXVhYZk828Vug}sgF6LxC_$D^i04#xUEr=#(B}X=a5G%Sc zRd}h&D-n*vk-CGKlDH$o5zryG_pbxNYnib}gQmXi8Q=^wPefN_nDl+Y#5o^t6I;KR znC3g{L~l#GR2dVYQzZbO=k&RDc|rAHv!+95rF82GSyS#GF~+#8X4C5aBIf4tNkyjs z2tK;LVoKO!#jUyY6!49ym`YKiag^mL#CW4F+k{QfEPO~IJ*?1bg^8QA245p<;YiWC z?fI2UH^3|OBhWz{Ju?H^CLPP~M*BLpWjLi?sqPxz4%=X{X>=CTzBlhWA_c9__H_xH z%!A_wu9yP}6V0GDKvdRXE{sK7=c|Jz_v6X3uUcluR&PRUY*+w~r2Jh-$w%o)7H(Rm zK)P|S$GB_B0HMCh&8nOx+tWIsBF1_bqU4p{eA`&n`;D%b7PD#ThJH6VAiDP{MWesgSCW}58;Z% zUS;=bF=X?91w1F~z}7bA?SO}#g~SF=(LI_!UPDot^35Lsj%lXRUIyVsDjUMEg$qa^Bz8jocP`z(b$r8wLk!bi*BH3-qRiq`3w#6Ag7 zwIYq(Jv*N$%wMOq@DNrcc+yXGq|^v~)O85`!3^FQTdSh?Vh9 znp_Q1m!@a6rX5Fkhw%D-M+0^Ewa+TzXhu?j%mkg;@CcJT>Zg5*{$-!}fE20cKv_v` zJlivLpeu~}hW6~&MmZJwHmO*JUB;z6mZ2G|(F+Xnjj*HZmroO7IABV3BYvAF<&au~ zG*db`Uy)}R`^kokELJ4tO22j#S;s>oXx5F;lx=U!}Cu z{0eTPDuQLfx!VbDN2TM7Vc7R;NqyjnVhD$XL2;g{%_GL6bS^g zp6J0|SJ5JMbUTh|pZKAJ93O6>thM_S3V|fB`1XOR9cL|dbes#nA9;Zj{MW{#1Tvw5 zuBxliNn#zdOX!p~{x3H0cl~LDN{_Dtyg9|L-cx(PftY!aw{0<%`+HU%%vG*!Dw@Lc zZ8iJgcRgtmNsEhlIL&!Qb1KST9wAAi!=nxsnZh#j=0~8j+6g3X`IHRix>tnL>Vd&- z2oZTJ%CzNQV#0PwHT`ALLvLR~xdU{+87mH61SJ(cV(Bk1NZ>_K7O9)`k9UG30tJ&@&C~?Z{oMgxM0N0~q>|FVEY%IgZD@^|6aX7ZW z-(ie^V>^A!*K1D!0CJLy485CwsGXZP@(dq0oJxD25upvIyLVOJZ>$N?@Vl8#bdIPE zr89mr=Pi=zWud$}(U9}ihr9)!z$7-@s|IfvC)6DZ&?&wUS`NB9<$`4`nHX4BwnkYr z9RV}{V3zb>jMI(YKR}UN0DU$|&V!1VTpW2PeJGY^Zaa@>x8#~vW9PkE!HVMOOIFD# zl~{dt&vRO;d>}k;nu|4^q-R1@@LkMU3UPF(?s+`+U_Zd&3jY(aq zR7-2iiRKB!Pn#PakvRD21I%nFt+w7_NY6T7_K=b-9Ut?*8 z0(eq~dfhT~)RO$}XniEBR~h~{T*ol+n+Ux23px!6Es|G|>BK0m127$oKST&byX(Iw zzvV|4wg-#yJ`6uBOJq*Ah=mGsO;sEu>X3Qvnd~CIvag0j*}aKyQ@yXA?(~s-$h%el z-jz5bM{9QeF_b3;shd6V!pxo!;qeMOHZa|VdLJMC>;0h0gd;ASZQev{tB1s*t}I7XPMZm4HT~%XCh_>@|9vg$U*v z!y1pAg<@ljtpwj|WE&WKVmx7P@HJ$H@i^bbE0>;6r~P$GB|@J%-AS!KdY&YtJ69d} zab#KA^Qe^N?OP;+sPdK8P>OtDWAkYW&U4xrd_W(&=Q+YtVvVnR{5*Sng)#$zccD#- zatd^Zs*ipVHfVnn2F0f)MJ!~E#i;8ZwoOo5tw1l#r37wxAf39**>Q#K(b~RCWYl~h zjXb>m!AVpQwGroyCwF;X_Pu*8c@3o;*W)E(?KWGAUwDw;`{nUMOD_=WsW7(RGXO2< zOz%R#S>2TNJzQft&sJqTxx!Rts|Gcfl-!%ar67jC-pKaq@V{;*&0fZ?x5lgNI>gns z&ws22`DuH;fp~XnfsmE|_zguKyzx-)Zu7Dyih@p*^wajCiu`zecIklgNisg$3r<2Q z#IA0no$amEIXk_6##+H$-fDf|JsGt;?!KpL9iQVPyLx-I#@u+4?zpnH zo5n6B*=YFq?6+%H9AS0Z=WGU7|SO%=tvcI53eQH4G?bq!aXj&o~BcgF7u zoibLVf)JXsO`tt6 zGe?Rb->6*hzm=?FuzVOy=~6jgWcU-dk0ETON6b_JFK6_7+BxmdX^x(>uiCmTR*a1Wg5lZ1)2NrD0 zr80TcLA4l=Ui+vFaibj*`Ids${Jp^71sn!_*qE2zioeZU=6GsD_~J z@P*vtZ_Ahu{6YO4vYH7MIT319Pur_PlOpZGfutG#ry}s-wPDTs4PVaTTlLOE<*C;z zx~$Asdsch&q{>_*%&jR+r%kbvvK_3w%!3aqvyAY%ZVdrC;mlj5QZ9$FXTruoE^@OB zijQFYF%x~kRCDAYHCdmGtyxdY7zW*qqNnDZ%(FZ-V>f3oTJpHTTCmH#-|FNOE63A; zJ69wJm#-REfPD4``1#Ex@MeNVw>QWE*#S*{UgPeR4H)XF^Zb&;#`|P1?Q4;U9Q{5$I#ic3BE!>EsQc1;(GZYwe` zOljEqKkx3nH@E)hSWEAF=0ZG~CqFfko=V*T-YEUD%pbIrTF>a5dsq`c*VzY`NAwA7 zs|vdxCA*P~=Zl$WO;9lf#9?e#>1|o{j1Ex`;ODaPm&94{{yIegP1#6P;x%`d8TFWV~UtbPeru$${wyr+s*vZMs)u zvF}+n35>NzI>h4_HDg73o|kLu2lo7RnUo@wwg<>odNLyIt+k;Afip11Rd#9D14Kkk z#UrysWPiF20q4gxSZF%E47t?I?Hlk1+2I-=9nOqIqXsWZJV!aM))5}guCXv}Db%x+ zOY8^nlq|Hd{!C%=XZdvfiPGf{x=<+@Yr<2f*vn=Wa7RNN4%e329iL@ZVB)^M>t2*( zSt>>d(d!^)y`NiI_qf69F7d4*$e{H;>)GwxyutSk_pI)s27R*)`mV%*4?;F|8!7>g z)AF(-g}rMnj&cd^g6IL>K7HnR5pvgf%-zN;@#~2esxJn13C?s|{Wi{3nWacd6v1c~ zfK@)ru|p}BtsfBd^YndtYGe~MGbDd4I8X?GCRlJ@-13xuDG);AaDA1r-jk4|8vP~Y z+BlHqWK=GSD!SY3L@vJ94;yK)I5u1M+sr4Mr%q;c`Sj|_6b;`#ZKrN4Cwrvi(mr33 z*t@1W(O&9>x&lk)w1`cvX?_9fZ-hTHu!s zc=StBpQ3z)OG+iDKRNss;kWl*%k6<4Yx^~q8Vd`8inUn+O3)y{At$+aKdMtnS!>j1 zLC;~)+~0Dsa-#wn@D64EMar)|N*6v*6tFQvu3V4E5MqxjMO~XSO&Vth!`7Zpm8Q1$ zml_;XmOe-DKd@jx;u3`P2WR!&+Q=5=jpll%^J3N?B7S+t?8{pM(p+u+;%ttuZn3Tz zvle~?8{qeXj|wT?TWJn0R?ia^Uw4uYV@Yw73~vf;-)j(6lF**xHYcmKP#{h(h;M~6 zh^qz22eUCii+yfg{=pn^SC?~9$F+Wq(zX{#A2LnGL^s!!3m|hH=Yp?nktPKp79Uuf zEvjVtp2)ew_oaK=Jnr~8n^W5s7mlx>Xd;9H?=rmS-$X`y_R+ZDyBX5nwF^(pS$FQ9O+kzh6H{Y|) zKNr+E)aP_=dx45)=O%Ev-EVjN#&Z7L2n>h z7_Pfbj`mI_?quNZFbQ6tIs1)J%6&E300<+#P8%svFPt*oj#>6y(7fy%IPcILaR;9x z%BK(iz!Vqf8>7IT;*T59u^&-I0>t<0*Qti(S*^P8sF!U4bNLW3>xr>;pj}Wnx64cn z@(tDlZbGO$8iiP8ST%=Nfp|xVoK!9ltxIGTv1#2!0V`@zj2(M1GZ$&UT2{{tM>F`# z5Tp!7xe6{ zRX%v?y5U4;U(cc;cE-+_xRPrPx^CCBT@MPjmMFWiYEvU~+R=T>6$04nlPnPO+IvpV z!__86^^>J|FD(N>y5Rc6s|@e7UccTIHq)b3B=vGZfZ#_C=C^-6Z=yxpgwGn?_1ED* zQSFsAopK9Fy_Ga*-6R;Ya*e8BDa45I$bI^x>K%Vo<$K_5%UFMEV|&c|NZ{6N1g)vxC?d7fb(Kjv_b-gg#J_xO~fL#t3+SKI5c(t1o4P)PX28Y7pKJ{`h> z$((w>csnnuZ~MDB*S*?*$SM2U?=*YX(U-W`r^gx;2m!(!9LrLJvGKR7?wv9l%xfJ@ z^NNwe`dnuEPNX8>V3J^fOacfx1xxqR)R3jB51h}~X8KEOMMQ-;+0%0EMf zGBYNNs24EK+CYT?ZZ}%k3cBYFB|#EyRH{mr;O`|6SFP6f93;K99G1Z~P44}RqUuDo zE&m{CclSg2lA4-*3r>nTZGhywGpS%zoadRumusrdGmO_=;eH*kXMr~^@V>sl#j371 z2wNp}TM1va#+~QJIKnWc3`L6ZV`Mm1MF+`s6 zUb2!-1EP!O_$dsvwM6=&ghm4zNozO$!<*R|^~jEmon#^dJQYKG&EK?6=9_@CN|I%MM*wN<(4@ER>=Z4}=Ioh?t_H=~lh>cW`c)#b1 zW4AzESw{9<{WiFW-)0({S2($QX6oA1q72nq7DmOrp=Fus_iGMDTlA&=MAZ?G5&PtpvLwDAgE3OpxO1%u*r zRzsxy3LOkAr?eNRLSVdEz^^5@Vw}^cTp4n6bVp&~P0Zs^1&C`;804v`dBDywr?1bP< zZ|G@9Cp@Vx-`MN2W6zUPRSnYG+TwOOCXuI>AEGfZkOk& zLk0TUGjsm(T4|@L!hP5-Bu!dorf_#t+fMS$&f#0XPs$Ue*PgYnJu@3qebfuy;A4+u zlIimcjGQUr{`SCgnv*FJrZn+kYR(iJq`~)eRh!d&q)WaN)XYm+|D?3>DZ?Mx&Ia0a z{pz)bT~}8$kSiL-oMxm21fm&6S4gcB+g19hC-;%!O!waY4Hl8Tz95+k3+=oxAl}*mRJVS{ut1I!;JIgGQ-iTuN6ltSNR_{T^0!Pi4 zuUp?@@-mCn(iS+`JweMX3tV1Q@iL?J5z6{Kiy~3t&vdGd(1d7BM_B#d4Luan~5Sk*tpCLg*haT2F!kqe4TvfYsCUG?h>i2U7>`tc6+ZY-GRN{ZdIv8sjb#C7(i}l{ggLIvyJ0HwCx(t|JPbI@M zH-{aMnBT6)JSd~YUn)c@^T|Ii77%}DE$4)QmHD&Q9@V^o=59n@gpv7>&;BXu10GG) z2qSw+7Fh0TGp==J?_8g1Hxh3eVb=11k*Hq$r^B2b8kt}Q>6TW*G(|qWe^$Wf#j?u(kFhpzdi|Dlw8=9O6)28 zbYpx);n7iS>*5!oz_odp={Vuw0?__HulurH?fiTN=pr3xAX6$^&)Wvf7=DajNrtU@ zz{w7Oh%ub@vCP@kQ*-#?zM5&0+;QDou+I)l*R6Xe3n3x_MhMl9eYeJXrTQo(uJ~+Q z{NL2tq8EW^B&zsNIp_`T=&ZS30Wpscb}#(?cE5a+d}ZLa1F2%Qq={``@EWXZQjuLz zw|Ff+mh21!;waCB?Rp5;ZI)$i5e3n{fxT^$}`ikR0LQ!U!SPjtX? z+6xo;2On~6m?uoxkt@xRxq$6uw==Rc0#{d98F&zpVe0!J*H_uK5e6tQz14>F-Oood19CiHs$*&!E*@$e~3x5hZ{nbzlG zCD~gIl}c~sI5X`56K1;1H96g-2dD~%bqgkBbh@c{Nc@`hCs27kUl9UBtto7|2Cz=s z0QuCgb~l``QI0bv8XybImmFHJc3+FstbYWx#>0HlhK?q0Aoto)G@iDgJU`!>#l2v1 z<)=Pd8m918>Jm$?`{|4r2av3pHjcQ1Ld-a^9Jod_;$8P}9RdQcvTdXX!YtPs{K>x8 zeJQIURRST^wZbv54_}gGjLmmWZS*eBwR0PlNH^4;O*CS{OI3hDWbC{3zB+2dT<}2Z zv*}L!cC=2EZ`nYrKLx55RvTR5#qA=W6!CyGt_EFkq}|Sen3V6hr=Kv9$tYM068FPx zfbNZV9g5vH?qHG#yK}u{I(ozF^6}7@Qolf~_Zq_u!PKBT7h`26PFlkT;)USsRWLiO zdc&EiK+hkq$c*BTm&FU`Nysotre}{2Dr+?Ka;vcz*yN(FEmS;-6i1t(&QNYYGb*-~VM@ccKYbgcsy~?+D$DFAIppk9XIvwmjAvmM(_-%u$1hJnnXHHy6U`6ag%B=5>eJBRmuOpjCKa_0ZD&dVy9IoJuMaOUho{0rvih}) zDQ{9&&-1GO&QVHip(wAVC=X>Hp}=ANHWNY>dS?D9tsc450~;;$EyTV=!2{dZi5n&F zdrZh^T}*m=1Wxsd5PixN37RxOOlOwf7`%^ejk=68$$chy4E)G;Std3-D8T0-jh~Qr zBkJgwXUqNdhC}YJN+4@}$$tw8C~GYjgRglnniKXdEQJJNr`T!@>`Nc}q~^5U5?4D^ z)hR%84~$T@KXuf&LYfQo7{k}KaGp>3R&ztIAKdGBnUujwM}Wj<9@P%$O!czzbJ3>;9v_a;d@!8o zLwbu9!-x;RF(Wj}vBFd-jgXUEYNTvtL!AzERi|+7C7?B^EbvD8HlHqMb4*|U-SWtT zP$`|e*ax`MIkAnB3G-IbU=`t@K!oIP;2`lex)C? zsd{7m&L;?`3Im~%;=RXXd|V&5^G%Hx&6$?uE@4%}RGaO{^D#U{{_D?|?Ub|qdf4hd zJ2xH-!IyF#?qVQ%qUj$j$ry->Z3Fs^+&0J|E_T}~FmZi&6}xsU1&alW?@s1ffhbn6 zt6@K5^0urm)nu{IqN|KC%qSy;LFk`jyvdDWJP%58RKqshvy4%Y%fPL<1iPlfQzI5j z5xYG1Vx&BzlaLRysV@xrsnFVufFtysbd5#}-E-K6$6@#ZMiy=WF;k~0Au87s zh>>6fWIz+T&uoRj6Zt00c@8F!ahVylO(Y|E#JD6sti^2Zc9G>n_g-&t(O?vycbnzf z3j7F&H7Oi-@&g|u?Why;PikW$L@4{1xl*_$=2i&&Q?YaOkoqF?&f^ovk+9_YECn!yt1 zlGydE(Xk5YAZcOXa>Ort{DK?BcI2R9FninRSrZE-(tks7=w}mbnVV#_i4Nu-yMP|m zYM}J9*41HlxeC%gHss8!(Sc5ARY&8)Xk2AFAL|C}^>0?@S&`4m+7A6%G>1uOyUn*` zip)9n+)=H7qC&k6pZrXSfXx}i>P`=1l2*-`jw>?;IQ4=`g~W%i@s&2$l=!+3>&Mz$ z(q3gUkVo4BQ0_j>>SKBK2`2d8D@o{x@G4Wl$s9|xH1RToe>CzhC|o*;g6&paA}?Lq z>AQG?BE$N7@~D(Wrd>mzDOj8iuvq6w_vFVHce$?T&h6!unuUq#LdIe)xE}v%^TU4xB42f8ebFjgPpddN}I(Jte4q zfq#P^CeP;D?(W;JK=wvm@YkhNqiY$tNR>j1QPdFjVyR&EP(N)Ue#FY(V@I@+pzg`JFx7FL#iRc z($?Q*__d>u!~;TS{=(kWEL5&zq13>URVc}P$RseKkCwr#J2!5UgSjMix@~%JG&*-) zES1*%HZu$H#|uW={!>8Xnc0DSgTlZpONx1olz9gKAmEjTMrTav=A?&Ed_wCS zGxIW5>g7+jWnKGr?lcE=P4(0txCg(sp1gs<;wzsn9UN`xMa^~`#B=ALe#Vwza3P8U zrYq=FX#`1)ACKJjp#9l9appz!2{dV9dv$vyeg##1zV5!U7au!sD^)Hll|Gw!OybtH z{qInEqp5v6Wlf%qZ6?fAwAi>3Jw2-8H(em{-q(u`H}d>l>DQS{Zx;+E8f7Ru@{dBR z;>p|42G4p?s`Q-)1I~)y6Nv-f%pg-=w~+j?Hv^;Qp?7tnu5MsDy_I&g9346Gyw!cE zzwL>8(DOVFod#1Q7WSw1Khsww&V_2(m+DeudAM9)l^W3ujT0CB2S#u#Oc>vD@cL2i z73!a=PfiAIwGv6jKFH(Ge#*5`R@;qQE&_TJf%72%gFmdLn?nR#5=82Zd$C0bRYo*8 zgN51UdDIDhhcUny#cT|&0;S<(69bn^bj3=dnN&ZGC7%4(*80(p7sz42lJc*@4Z6L0$cW&t-fTG-tB~vbBE0Rf$+WT zp)uiGel+YucF>T<56f1`P`P6ic?vzmphpuKt6{?{sc2{aGK689K!_*ss^aQD*G|F3= zZFQn(#wH>BiPuV71_l2x0uy;`!7<8Mqzjk-h|^VbqVCI|bHoRt%=DanFGYio0zmt{ zKa6%CLY8ZCe>}K4cW&{GxZ73BllPgkZ$x=D&Kj()RZ~tQyd$W*QfDq&JD#@1ll)2Q zE^Ct759nB*KEWBG{>d7ihP=p&h_bq~_nhOJ`SJgia|&M^!bov|-LvgZNgY04|L78# zk#)+FZs|zPJ~qMZed5`kd>~b4Kw_gk+2hSSfvq$Ck&wrYID7usE2$_BOvGFqP_(m- zp!J{wdWo6c{b(5m-Hd0px9xqx~1IeY`DDOl5dTn zRoNG`o86dGWajAv-==qlIJu?g@#6PPj1mCrE(WYS#)^;{kYHv1pq!?{xBARl=W=Fg zPoDdjW2&Uh+m%lJNph;w{;{RMX|Td@gh}5oO;4+?0bt7MQ3>1v)7&-D0C~+x7fszg zNM0-Vnw*-u`1H0EfspYbKho_(o?W7!D1S9`ckQiPy-%LR!vCl$7bkp&XfEM%B2f7bI$Kb^v=aiUtZH zUc+M0$$qrcl-(f#cG3=K>5KPu+MgurPfQyC_1@co3Nb=CIaC8J|H*0F6WKtQJXcDmP-u55}sq`??g*^%yP{YTKi7O(L# z$h9*!RUN4@c26Ai<0+#B^G|V%>UVuftw(0&cAOD!Hcw!9@mQ$UXU1;OxMci}DkStB z6+F526oJoX-lwLZAMF$9%5%l4?6BmMwb*;zva9_h)6jpeP8Wc^iHCB2$;a*j4XF33 zA8sM3_;S|dld9YQ=(L+dX@0joZxKRF>QQy}s7weu;$hyD6A8->ukpJj@vpO+I zx_fUKWk3F)0?3c1mDq<|bsMeOu%MTXtuDC0Gcl#<7Q*9uM>wZ#84NAAO!n*32fOH| zl5ljt_yYC-m#DIN&DrGKD~sDIEB+do9>S;Duhd^~xzbV0wt^5SD)^HO zX-piWPOsgiH(FUjEv(zOh&Q-IE!gnKbq~ln~UBkZG%X2v7MJZvNjZ`)v6LhbsyM^V&(ynQ9|yX@>(9DUR9S_?PK6I z)E93^lDiVWo9RoT*kMMhlu1p1yG?6z@N~o#v#H;J^N4w;lKtkX#OckZDJ4PJhicp9 zp;KvwmaWy3sI}SP ze|ANx)wUm(-)0EJ-rqMlSAFHfL)L9U3h-W@(pM6i`dT)!Xoj!Kqupx?6OeUMyqjLM z(922vWD|t!8^p7nD{QfG{r8!Ht@(#Pp3O*cE&g5dKeroJDgF3MCAAy8Y9ahAQz}(Q z?Q?_W*6hkxncV&*kQ`s+1N3c**Kkjg4?;Wl!td?Ae@*Si99182lnF2buX54VjetPPD>D`-9A+8iEPyy)MFfl~?(ZDHOQMgC&rDHZDZ70gAVIm(D%QbbJU|nrycR6@g!8DE5?>q|pYQBnci2mS z?IeD<130V?2$#N4_r9X{$wE7P6R`xkCu%K-=0iKZ|UjthCJ`CXheJE%3 z3bKrg@qx;>+Olt%h;AFP$W_xVSLe<*AVhF_tCkvOXe@6ROi6bNNf# zMg1vMBP99j$WR%5M{pfW=AoVIc02I1q*Vo zdDm(@{zqRg^{LkbXJqwOU$0J^7P8V_d9yEf-lAx*+5Jc#aQFor*ZBO_I<8e&0(TOs zrjzOrf3T}B^RTh5nn!0|g+?}HaU@%{&WoI#*+>EhfpWq*6#{&SX`iKY2M*Vj1XreIHv zDvOGDNpA=a$}tHm#XAx5Wg~`WLUkOQYe^!%_V-16#sjGyXSx31gz5&%p?>2TaGc|a zwb+^>ka+x3*VJY%9iC7;1dMG!a2CUdBhHWRhqgNMFug{wH@<`b2&*w@ncBi>8~=G# z%bLl=!YiAr(yhzM2FW2`&@4t}=}40eQ<2SpvB(Yqm5Y{OZ6Ad9cGfAg_6OmTol1Q# zHd%e&CN6-SP!1{LYBN)wHl*%pk+eM<)i3L#@K+_Z#atWMPsA5MoBqUvo*zxcSN1A0 zEd3{J7Nq|ADX2SB7kMnTGKME=3~1f{3Pyl2%}KGcz>I((rb*=@B+9Kl!+Ev4l0t4@ zV^wy-jJ)PqEYMkM4abmB4HeVB_ELM`>zl zxfweFj47$HtD~4Jv60;`%l51-8nc#9V>!cAWgCd(l2W;{{E6F65s1`EQC`GDn~o|U zk$@tF%3Hxj)hqiC`Frew93a0Z@hXW{tgOj8k7AAjMsC>3&ZK_4n0VV+-j`A~W3x{< zcpnhTnJ+O-wKnsME=l}rmYLw?DK2;FYo;Lc1>DAvv0(D~9nRd}dI5-*!Idq&Is@pRx{o)vSB^cstm-p-_-<6B?QXk@ z>q#S?f1Q5_PgKkJ3^v1~u#83JumVfCP7!afU(wNV$Kve5V!ekgvFQU@+F8{$psa z)rTvK`22jcBKnHnY};QFE1sEjaG~V>Cnn8Mps$7OcON1e>K7|)G&>dP%i3QpiY133b!n$7yXMXrB|WM#-q=9vFk=HJ43b@$ zS;W=M>in{!^=|E+bDdZy;DBqKIl2WjWt=%R%xj$YCFW7Z?&L~oVyfNEnwv^D#c=Uyw?M}!sTzt`=Tg}0RiM2`7l zm+s~a=SfZa>wTnbus^`qpI2JVYs{rp#O%R#Fcs(>nk*UWvMa$c zX!nU2i*S@}(bj-~vzQnx1m}8jcn7iQz)w5s?aac#SM)a8{*vNwD-h#%^K;Hv66ywT zC}4LE31)VThtRI6#O5jWJMK4``dCvPP?#8yV`4D?0WWW?@>L3^E8}2?U0P1;@G`9Ijg7aS44W~uXZ9# ze~aa*HfgJ<)tDr4N=!vqjqUrhq28W)L2gmM9C{P8fNJF1Kg_E%6;r+&(WvC3dXy6* zTY*rPo3o_NE;m56uEzn8Fvve`KGS^IOQYS%=&-WnrU+Mi1Tl^uj7+S+zwu!>(=Wy} zyM@xAcnizWEDi9eetNYkSc8(Y;5#-0-2T>VjAXsH4s6mZ4m568=ZzX!o)ywb^VKu8FWQ)Mi&9>qCnHss#i;tVGpgvR{i(eZ29gh{N?l2gL%zvH??c? zqkvE@MFT#ey+3!=*WNI^N*hr7eN)ny%yzH%9P1dK^^xx^yDX^G{AHCz1T!h`1DRuJ zEK{g0RBER7h!0(A@9K*1{Fbwe{Oas3SPOr?vW5|9bggxkxkg0k3!nEuM^ zG~^n4c2_nBBLk`33J`p9elH5%?R8%(vKk;W*{S2}wB2S&N<~vU{mSEXTlG*|m&6>M zD=oN_?&k4oR?uG))o{sbk?fOo>>pmbPHN`(v2D#iX9+WgkzWi!8RSTjZ?Qv*ultbi zSih9rYbhTI6$N|D9X| zPEdTek^_dFu+1aN&45k)2p?_wK%a7-Ved{We~)F-_^63sc^i;lTw4izpmSnx#%sbK zlFT2}{cv5ah@pGoVLr1FJl}j5tEh=8v`7 zB39aW*RQSFwFFo`U>T#r&X4xEnKz~q-3+jON4AXtu=_o}=xPTM+tov-B=sjr3hTC5 zQC@tAJ0awBFx9>$Is2TEIVD|44LLjTmc1oMmoT{@ve} z{*^^IePEGWiLXxuqS8%(K*6pAZ-)>pErs6>rvCoe zt6=9ltQb|^EeII)@^>xp0`9-H$Zf|r06(DnozcpFh5eZpK+}@(KtZ52{R5gDl*I{k z<<@h7NrIo2DfLcpJHp(AJdOzkgHL)~6r4U_HM}9HacEH!zwIpCvcT&34=?b;ZY*5| z?7ELX?L@-wsg zZGz?Fon-C3LQDNJfdA8XXEgc#y#E|^zFgqEBY~6#j^%;JWk}Lw6f^iyE0TOSz&D!k#iPO@UO9{-<*8RX1I9mI4D!^adklKlD;QZ>p z((p~vfU^vRHw*;h_Lba07K$2-ZTcy6eEnh9R@M1T@t;h7{>$PEdrpbl<4nQ_Yz|LO z9iX-^Fq5;M!2`X;cLEEefM2^)XH^}WS-Gz%K=%UxEx`2)hcw>W#d+&-IfqL5?&Uez zuf>P4IM9ayuEQ+QsqKW7$XG-61}_20Uq#Ckp!9Q3a!y$fuESL(#?jAQ6N3((mYlkM z>sfH^%}?^=zvATrDTM2N-}GXf<{^Bi>|@4*9&`f$Setp2WGLHSs3!h&X#JN@sZO|5 zoLv#)XK(Yh{5%5ADV{HApD&8!n4qcG^sc#}f#-F%EDH_I#yN4yPt(R6O29gZ^ZNIZ z*4A9JohP$xvr+DNwWFU6%g3vjAe-mKW+(nhPLu-GcFx`>G5lUV32#>@s^!V6lPHf% zmXBmF)dd=R+5a@kyckl2JfL}m&t(*H;3B!49rWj}+N*+~Se_GmS2aVY)mRtJF73Gq z)EtUp>ACN9vnzH*c}I`NS-Q8-?qo#14RarV!zRs->{zg zjI`jCqx*aZ9~#xS<;L)=x(A&fwaPtLIT0Cvy2M*2!^NhZ0@Kpltz7<$T89T8c$xx= z=5aeK*bqzrsWWcx+U$7)#9FE6a#7Gn&xP>kOiS%W;LjQW_39b3`|Uem8_#oI9R3K} zwg?l*iE^}qF`jkaItY*X`XFu%m;HjASKMp{ye!IZx<+&2!>Ttk!a&?4AQrTpRAgFs+4J^YV$!Eo?sLBd`Wxo)l6QY=wAR zd;RkFKLww54LU!&CS!T@yp0ABRK-uegm)TzTN$8Waizq`0Q9%im1q*^1%-h59bfvx4QL zT;ZQ96s!Kjk}78MMJ;;l=sdh9_nCbAx{`(qrSJe_69|qB8h(26B<>q=(LClZHVfpF z6!E~ctgUdHa7QunyF$i)r}&3Lu05$XLmtp>9xss66#KTuiUx1L7DImUIhqH($rW*)pEuLI(Vw8}UwvMuPRi-=tDBAAgwh_IimnAZ z=>hIqN$}qno)qWJ717$-jk*Be%_H-L;Gbh!tI7Id%7B0(DaH8pcM)Mms(@SWw+i4^ z!asQ8HnIglDlW?L$o?tV+ZT?A4 zr1vsgh`_D!QG&CXeeM+V`3BsZb2i$5$t$e@20y!9l-l3s@VM}1^Om~2+7c6IbQTEB ze$qhoUH`C5OJ5l$q7MJhnw9|I4qljpJ7-6f`L>BkNko4a9H8IdeFqr&90LH+S0-}I zY8o)BvMhm+)Q2BfVmrP`6IdL$G-iM9kG!L8#dE3z=tchiSZPk?!Ks=8aRgEN8?_we zw&j@Ak0sTz$*`NXH=lhoS73GEa+S>>B)G~}koU|lWU=i_`>empZreX-m9WkEJ5|;4 z=Plbj=GiPpa~$VS`vrym0|mtK_v5E7Sp59M5&lgG3Fg}trXj5U>GL%MiLb~6O5(T= zywHEEc$C_oL{Z$zy;U&%Cm2`(=rw93+VkIPoa_48v(G+%V>{piH+=-BW^ipU1rhQz z<-j*vkLGb#|F}v$@Uzavn?qg>Bnio`mQW;JFMY%T+lH_6&~M}1~bM% zhdYso9{@FHkC-(=R*bxpB+z%|?uTr#uK3iy8>0aHzvYQ`_7Cm*VMKh}(1N^}ICV@) z@HZR#2owBxageN@^55bDmicUKY;8*&t8S@pq118+*94>CcZBu*k|G^XTN%aCYZ@K3 z&)=BZL&}>xYO|aoyc_!pj+>9&NSC()J~PCm-xv@>pR!y6KC{eIK{fUHyqbAxHXyd} z0_AuEQ@MW?E7THj?GidqW%#Ih_Xnp4v-lhz;4{U6 z|8JK9=2rcmrV@YnHmk|c3h;&nfptcwoN?*IpxJf7s&MQ_V2eoY8DMXX3jzRqOR(Q&?!^VT;3QjL=-0Nuc!0OHoiF9DQZLHu9#hfi-1X+ASG2u1!#-nXR;&)%vr&Yk0rjxOx^n(!@tJ^9Pb+0_Wvemlz^Z9nH^zfT80L*Vf>GS{`DVl2vIJ5AnMhN!_wRS?VpPn@ICd`tN1x{mm!N0G| z$)T)-pVj|y>3<+ZeK+vE#CyNZJ^oh)1_1{HzzEXP&11*@2SPIRfbVthdM~l(UqvSj zl<9z@W=Z*@=l_*~=SI!|-*b7BQXunhf&;_~91tq;*V(;K`%XxdweqKLy#U`o{KtPJ z?f`ys<@8IhbN?npfYo&}z>$zo_RIf4C4;-Af$uqnc18ZLLjH$X|5qXZPb%@RN=S>S zl3IVwo^CYQ^$k;J!+5NOXZ`2WyuMn*M{{0_jyVUtshjHiw}K&ILC_cDoTeM!FnWEo ziU5j+?|wY?#pi1(xkq)i^Ur^NWlx{5%cKdcd}&-}$G4zLvYtC24>$y?=;V>ydRqTM zzQ~UyXi;9pY009uBL2CtTXkKRpxrFs3Dd{DC*}t<7U5DGZ)0r)tauS4JW@W4gupSF zHgnNH_tazZ&61NF;$k)Ce$O(k56+FGY6KZ0JoOgu|K&uyv{`12<}sV%YNPWf_$%=z zqRo(K8j-VhRkFHEnhI~}{7$f^tFBUOa65Cb9qvaU4AahdRF!)q6pv=*Y|bM$mi9cNtJfesoKYHV!`S47g|k@Su05`@xd|8)WpVG{PN z)qj{dZ&gCkf_g>RcD+#BpI~ZCEY$G-!lgSP5V9S1di7K>DJA7Ohh$I;kGg{rf20PY zO{;!x52)dSI`XCeIRZ36U!2x3+yo=93~3zYt8*W@2;uke45*Jn-;@x_0+mm>Fv0YS zO7Fa=DhfnZp+*d{*f<_!CG_fGQ#jg4;<;_2 zd({wPTV<2W0W0Pi$TL$g$*HCwRx>N0?-xh!mhIfc(!c#Oc6%5O!Hr4n#c3$rgI$?m zKUoF)Vy=r4m!>XNbjp)KUDFO-mt@kC_lpy!+SPK+MYTBUXC++jcHl~>V~k)%N6{WE zU%UFN!He|=Ydc#qTmM}ZU1%ZRE_P_5On0EKDQD>lF!$J}SndrkU1*0>&A{Z+x>Cmo z?5!!!N%E~a21kH80yNr~A8i|(FjbS0UZe}2SQyYxwKj)sxRYd)d;)w%=ll*YXlP3{ zFZCq&kq%y9oWv+u`nhXV*#@*7dTfv^N8EPN4{scwt>xBuO^Eg!<#1&BS!8)evW7AK zKzOVhE?>eQ6ue zd@(e#Zzqp~d9yg~U{i1`s0tu>aj!W2u&lx8*#Mb@o*ct%8v(IO>7ayFEFxofz)47G z?w4rmdn?+q=WxdpSj$#wBLVv=(>LqwXsf@jM2%_1}_T)i-0M2v)W2 zsFL=IB4-Nuw&4*W)_aM&ZqZh&w8Ud~QJoCiVRvCP(~)%2#YXzHVODMo0nV5T$7Q@`(k3Nk+=ZL0hb zltP~$e?QxgE%3`=y_|64O1$QPZ}(pezfAcsCEVETYh0d1jufi&BY_ zK&#aYAZIy=xKiTtnDN$2*OErACPt8=FE7`PJk@`eZe2JrdD0fXFz0wGBr-fAcx=i@Gq+=aK4k8C78|vHZ z!Cur?`h2QX)a4C@iLt0B-tgao@^0}+i%PYNJ=_qxfW~67EJmGPhmpc9JPa#356$t3 z^zr$&^IW*p-i@i%n($HUv1$rd)7ZpiO|Ld$svDQJ1pB;{`iUr53*XEl?9rO-2Ivqg z%5U!rZ`fn-0&qLLr~^AP^3f7`+^k}6)MW~3nVBsRA~X?=>=LD1k$6&MteoXo@cd)C z+;EfWP)F|Wu*E;bE!#UZvr$BEC^1<*2v8Cr$TSP}H|8CBsOW0r`qZ*6@Q>R_I~4qx zyHS$kX@M}3lZeB~&dFZQ$MrkYB|+9rsrrKaFJdv#3%_)}msg?&8?`#D5EWhO3e3nS zbMnjopTfR7uBo+IS4Bm)NEEgLQX(QCqEzXS=tc#kL_rNL6af+Gy(J)`AW|Y7q(r4{ zq)7)U0fB^~iL}rO5Fijr2%V6?4Z6Q`zwg}hyNC5(vhwn-d8a)yvu5Pj-EbCXlmW%+ z035+rYHl8?m_duC&1d(&(+uBUd!OkC<6dbFc~5v02Epg(cUEi=w%T(t=LBK&%;{>! zm_F{ED!d9@yP;b_PIDoueI{SE-*45W$c9D#rTIs{4VQ(T(;|6i@l4}ax(J*<=o+hE zNT5IG6e*Gf_AirqrV>{sF+dJ=%~{iBcgQMIqYjm4iL=i=;@lTXeV?h!bn0C*sGZXa znwB)k7O?~nR8mSD=4v0F5OXM)G9Hl9fdnoY=Q!=&7AxyB+qGxFJ@=z*-u{bLyHU5% zc@WuEM9ejuh(U7n@_jW;&KomRDTB%Y*r5qOk3ma87iYBqQE*u{_Q9iYg+A#{j`{CT zvKmfIRKM;GPax|Ac1LVo@VOrLv-!S#z#Yzs^oEP?_My}J=ji5uwT0R0%?y19hQrt^ zLRmjP&*?|k`Gad5Qm3w626kiq^x|0rd`x(4hTUSz_Xkd=RZOBZ*` zeF1jaTGT;0K`o8dvhmY-4R5*UPh!_Sqx6c(rN?8~lz-w?LG6T~sG@2J&TDDRg5_%b zi+4(F5eXKWF>hMvl-u<6R$9f5J5Kp$MWSjvp|gMMF$h;Tn^)x2sRKdyoPJH&9m}AE5M+X-IKq_f^@MpWh z+ZD{G*iEE0d~;z!udARy6TlKL4;S#xfGO!SLn>mRTaj8)AA6@s5hChyR& z`Dp-&%R&w9xGHi3QYKihdEii^`Z`jNqP^1x-nfprl8^9J{xsBwb89}=ssohY-oe~p#v?%{gAD{B}DlJg(J09xlQ?pEWe8Y zV1^rW*i-vmz}MmYRRtmYL^<~-{!=ws+-Y7tj>h1j3PY?_AWi_@Zwa@IYcUc z)jkT{5q{vh^p|={0TbD_cPU6ZoPT$Hd%l!e)F`#bLsS-RP0rJ&wDaADv1fy(ol7J9CZ=xAsqyLLDl9thPUYQUO?MRAg`sEsH zgtut%-u8w^l{PoGsTc@`Dw}j-hipZn*tU?w0L2_C7=d9 zvyZ3NRLWgSAugOv5A}|mgieZd8qRmX09#=0MDX%~W4Q@_j1$MsooAmCC$W)6Vt<4M z6qX>(lP!N72RVidY}*UQD*72iTb0NW0t<(2Pmt*W5D51xEIu-=QA3)AEUmf z;*XdK>lslkcVYE5keD$HW*{V??onu*=7q*a3%P9dh10lWpMpE@NY$)wAWkHIh7MCr zp&y~p6i{vu;RZ}DyV4^;|BasIo5d6#IzxQai7>Hvozthpmen9T8#2iMU28y-)oLl1zUHc-X4oi58l|V z>3H0c+$0^iUZ67{(fb{A<`B!^A0anAA~&VdS+bX=%UCc*Axln6nJ87vOp!frqUs66 zYU8`eJ|2J0IU~z{q@s3Y6sY~&-eDPlSW>NAca|Yf>qOG!M7~J9iTHN zyV7qheGW3_{!Mv`^-^iTvvNsk2Tk_FJL?vmzKFNd^9arh0w$v?nN`Ld({h)*zgX#5 zoaaT*qgW-H9Ntj}aOr>o-}=el_~^t+7O(IDyTrFH4S>us)j{gvyF-OqAX?Svps(U` z5!E1rHd1Kx$+7OKHh-*j)T;ICdh#UqYYEg=U$TO30d&>F>SwMNkn(9yQ7@>#NRm{i zxc*i^x#z+WSc$U1ChIj_iN@=DSAwEs+tAgG*Qw^;eP=|kZ#;k$snG&a+k}dc;D&RL zVJkF~@|BPmo1q~(_~FZC6-Eo%fhK^T=nG=5g^;{uGkW!o1axHaZJu6Y+~aOV<4^QHDs2P-Jlzw5+cO;W~)@#fFUIQ zeJV`Rgk$={5qlNF;as3i255Svi zqu%H0J1{zwk2L&K(&HDfYnW0VB6|*sGu+0wN%D1=8drTc-Sa3vNJ>?f>tBs$-zoex zzR(iRJ(X8rk?wk?YUh#S#+`wRWG}*b7FjOx_KTO9X!TXJ)(wAHE<}AEN4Vo$<1D=w z-)bSRVz)i^Vz(N_<(&U#%A^eEDJv>AA<{PbB1Gm#)(sq#48-sWa_-sP@-wSC`|9pq zCMQ~j^NOe2<~jS*7*)=ka&sb_H#Wq^x3V*fkDXZ63`oi2Zn*Hv`)OwuxbH_XqfwE7 zC?czcXKtmWG!H{!ASmzyp_@MSKH0A`kExhZU+jIT5w-D`ZM@=iORoVz&?NY$I6=3g zi=N*vydf?PaXzK8>gU=r?K@BG+$k1ht^Qk(qGOpFNZ2>*0E*s*^of=aQrlfEX1vF! zUIg8k84eGnZ;vUKEHu!cNS(L@9hq)93EQirFBLOP0X6r*XjX59c3nXPAogYhiZG5&{j6tHf0%cQ%-)7P(Ofv10(YD~Q%>CuH0 z+YviDuF{fj+Xhz^{5-6-69U#&nTaT8->7~^vm<_+x{}0 z3-LMTMvMq&eOK)lZSB1jA9qzFL9;~HjrJ)K17)0qzjM&~y&X~M6D}JHOO>Zm%vkL& z9sZLvO8h2pcSY=m5gaZm2;@_NdnGP>J35r0ueKCQb?)tsP-OMoln*}?JNe?GgTGcx z3-cq}n<`X-eoFmZQYe5^piJyzL;Gbg@RS8AG7q>0iv3X=_>i*|*%thW zNiw5Z(tNk|;8LI(W|$RcO6{2m?k!mIBCR1GysDD*w=)*$abIIP%Sdd;LE2kX;A(PE zHY#?8?=uSYl=-g85R4Ehp^RZni1!<3jX-3n2qc0xxtP_GzUJ}wZjr%k#!t4O?mUX4 z`L55OoD;uP&`lA=I5u}KWgY=}Tx?f}ayY?ci|@7ah;UvQn^2sNAYx-HCvSW`OBy`C zUaoumK>x%zloSZ|di~9+`#jLfJHdDMP(^rJJS~Klroz#Fylx}In|Wqv^GiK|n?<%U zSQ?>EAz`4JbK)l`Ajg^hLI{D9i-LDPg~j=dv;aESbpCR)GRl-IM12Yr>a4I{rLVi% zAqJ8xBK|??7;IxER%lXk1aQ1898-<8yc4Do$S`NX78VLDSyN@4AL1sY!*Tqdkr+Hd zby(RshdI|ebljsmt%bUNQt%S7(=dz$S|yh3*!|mmUKhG@RqZ5DJ00y7cJHfz3S;;3 zEDJ4rwi0X>pCg_}wc?Tg3x!$*{_ppRpA-F7PF@oWymK_9}1eF4g9-$CXI+Q9&21iTDAmF=1sAS@b8BaTkJh90xjR- zTOdyx4ng8DdM>Uau`7^pX?w0h*R&|y`xXUKyDsU-N7rib z*GitwTQXIg3D5L7{h*7ze452bB~=DJS@!)(DeIMI%|GeV`vySFW3DYE{7(6QnR^vf zd3;dEUR%h-{m(YK!-kw80eN$R;pf7E_R*fXne}(9@#(181E+(NdShs@i6fq=i;{E4m4M8N=#| zYma=_^VGxn1ro9|1+Kcav-+6RmQBGwgX!lq!ZOVkc)jHN{*#MZgzV6Q`wsy$`6{?6 zs1XpHvfe5J1Y;*XdX(YV0a2v)t?R&j6FY7spnl^vx-UbuTiTs+k2g3f7?QHJ?ZpVV zZn>k2ibJ^y|MaEDRmFZ}CfMu8exV)?E%rV$<6!@MkZ@8c>Xhos?ml{~B}M)3CoT+u zO`8(Zo8Q3fL3n+j=qe##vc~L6Q#Oa?8W#@zH>)3hnASF=+*{JR0Hv2lh=CnB7_M;pPW$Kw zO2vJl(il8ee1M&3*83)23-+o4HXtp*z4NM2sEimf?bRhUjchy7CosKvwHo_kKqLeOH$ zU7$l#3Kty>cAi*|Q(Es+4Vz|xjp$;3zq}vW540%JG4;N$*F)4PI=uF&syzem$9a%W zl=&_tn79P{_|$>wdRZ+K3w+|vQolU*YpEND!w+q57D`N|)zOo@IQwEJ>l?cgtDK1$k85Xu2qMY9vN!`1zt+8~RMM=E9diFw3AmWd_WMBptKiq?+b{QZ7k0C@#`~X? zx)t)b*Rb^_OH~E^yIyqSo*KNC5IY)Kf0pO>&fl?({r<6F z*K+BfJnp};80{fN{{+;IfK{&juG~?Z{AIX!UYT7#;jC5stDqRy9P%p{u$&PZ7( zGsOp$028vmH#5^|Q{!=NbfjetOmDMfB;;-G?}E0Q2gbo$p5U$dB~}+;M~6p^n7lnA zymB#%)GUTTu$lzZOfBb)zKMEs;F=Yjt4L+H)%XV1XAXk*cmls5E6?NWLx|lsliNp6 zJ#_jskg}i5wb+Yc^>z+15KL<7RIRAH{?9pnnEZzj8E(H0>QuGQbf#}tF0w4enIyPhrK+PQSP11SlPxxoy= z*AX?0srS=nGrBm(n>L9#Vc8zlu4*7EKOg? zZs9(!ttA-mb4xJ~c3WR^M8a6YX>c8}yQv zskqZR#??p>(x7)9kNo%}fN-LN{6%;ntaVI=>bbWbT|VCxHSjBTHz+N?3B|;=Oa`1i zBt&DUofUXW77RX%=CL#bcMB2E?)2#hY6^q??#yEK`~ITa9v$jHL`?-fiQ5K-|WOC z&S2b;?0)OTz*((f0ZH)Xa8D}#$j34TKn8s!+G$pQJu}qPxog{o=lB6dh1584p$k($ zvkR!+O3t^}W55fhW~GOt1cd@HZl}`F+0W(8XheU4uY}(;>xy%?27VD#5gd?H4`*B<`u|4v4mTU%ASTrQd|L9} zUR%r`L4TxiDSnUOo*m3bELOOA-I6qUk1~~>wFGdCH2a$6`)%%A!08#}DpamU>0gJPQ=au``b8^KqOHb@|=ltLg$RjGx3lE8|^=!@6-OiW{pnSzT zglh6sJ<`I^3H3UuQv5~>a(gvxM-GewHadiXR@~fvv0a>Ws^X>8|ijNy9UBgoZ)r;PFz=UX|jmAi2OoV`ANj@|8bc^FiL_^H} z0oQ+pUh7}r^-K@#Z$x-uN{4s0|6tr<_$_piT4)mO1ex8U1mWmS8nXk7M9!R9icUm} zhNGQlUa|!;x9?6l8#C?G1~xbwJ9I~ykHTkPS-qzZgZg|o8%-=LN!q}#tIq`$3=oSs z+>elcW&pgubkJOX3T?(|K&Gaf5S%PpBnV<9Yglp;}yddGWYN zeEo&5ls7U4{q0u9S3fGJPg})qfksAtWP|;$w%VG& z@evts;rKnVOm_QQpm_;YMHSHz&AwJ4xEMD4cAPKyV;cnO?-%Riaro!djoAhQzfi88 zfQ3FJodz-UFEegun{9r7<6GliHiK8Ff}R)s89HvpH=fmUALOA>=#7l_JaJY4lXT1F z8fTPlf2E`aEd-zt?Q|vqtva{y>`$O2(F_G=uiQJL5cxt8N6U=omE(pcTL* z+XiwQKe7|~5p+1^e%jnQX881Qy>Lc*3xEa^D_r^#RFh*iJa5`!S4fv>Mjx+kb*#gb zII_inHh<+dyJeyK*r8MKr$iyippS?rYikwrG;(^P?Y{Zi5JWu65AQ6%F~l#h|r@xp#VQq+UU zk0P0J>axo^5J3s;Zn2WhW9hCNp=FgtE1W`?3zxjwx+R?NgquR4 zsBQ^(WTtb5^5A>UyOqnOG&cqB+@eyuOHg-hv>^zgZ;bkGzw*RIYwN0JJ?zHMl4(e{ z{?NvGt^)-~XffV<>&(Oc1X%tcvSGQi#DNwPTaeZ>NZd{yBwp1O2|9v1aHjNUwCjKv&8nj1b^e9pG;E z(&xab?Xr(w3aAxBgz8N-rVCBCZhPGdTv7aycs+Mc2%VuChf*-CtG*K>V-p6KS?;-B zR>*EBZRg0F9*<>rbsWE+?s46B!qkhcx{tVFyN~kz!yksQ(lGW{&QA8<{bC(>f&C!f zqW1M7Z=FiphlhVz8^_%h5Rv=SP>aKxq-{O6aMutQhbF4kof1UlJ4_f>K&HXmR)SHT;+Jli`c zc81i7Xwur9uu_%a5v>{(z(ACDWyuV6e;Fy0l)f7>(XilFaaVBndnIkvThuo$%ilvf zrJO8!KlNO-au{S=3V(02**zZHF*d9#xYS_yoZThp4)txJ{cFiGqr2)EYP1>uBQCF~ z=1PdAd$nG4nt8b@Qg5h=HH&+n)papj{#9uZ*Iq^!y6dc)%+4M~@VLQ-%Smc8u!H2m zp(qXvsD95ZtPW_e+3Os^#VH#m$xhKYQ_9ppT|r((;a^GR;1M^;ua=1E*@~0rzdN_( zd2FNyQp~h~wXGw586$s4Z`em@5P3dO10xdFt~Dt$=O&2QZHXndt8Fj*t$LXh*2vPt zKpBz=tMmM4FqRgMDnN(l6F1RaFyA&)gJt%Y!7E(b6fqgvO#vhEc%^AfJVH!?UVkI@ zre8%@3L=Rv+!6Os@9AYp=6zP%8?KM4<+dDrwt_ZNt%^FYjJ5mPt$}=uc}v7>8O3*$ z3q4q&@9=$4Cd5d~M{FxGXqX=T)$eQQ0Vpe}qvg7!8`Qrbhus(c$h}uB91)hZJMp6& z!Hd%~r?#)v-i9&o=}Wv--K40?;GgXFT)Q}OVh|~Apm@$aQTT3V!D^@fxb|}Lz_f?6 z*-N0``%J?nLl`wT- z>_sK&qt)rRt}_MGXZ!=sMOHnwDMuQ=IRz{TcV8@8=S^b{DY~rB{E*C{WuZ1>D0Ed- z5yAPopzkO7TWi0O3XvQ3KT2Cjsa>8gn5|P6hPR$afP9R>11I=53vqfqoY4I(#W_{3^beMt8HPB^6p^*f!qZBbUr4KqP+;KJuG2}ZO zCR|ZR9r%a(eth-G1rDz@LpRSV?OiEH(Gy^8A6zh@QcNfip10>ZkRk6*iLCvxgQ|o| z`&1(A45kv?c_B%IPlyeFshShIn`2~i^cM!#t&-R^=>2OiTK8~`wZzI)QMuM;YU}n8 zbTAd*u`G^V-MT~kV{Ltl+Z%i&`}z|Iv3QK3@8)MbpLc!1iUePB+-YnHJvcF{S?KaU zI-EHad3@Eyqtrhoog>)~r#vNsxT5QjiP>E43D;M7ma{v?{39=CEQ>y*VvYH9eYV5j ziq-X!4AsAc(RbT3|h zraT&K8>NPgp{P_Rq>(RA!_(zGOR3KzV0g&fi;}g#%2qJFP})(SQDI^xM9M~$hd5>j zZ-yZ3Md_K+qgmXpv*qvEn5|8t8m?b05+VW#D)!b?ZLt|MGz3)FV%pxpyy+GXI4YK@ zqh0Xh8z4CD^~tBAu7Al6>G8#P=-t1pV^;|QHbRj)-mGz!=#k9L2-*qmI62;x`&#GI zm$*?8`JVgHhN#xKwX)j~$t035c1Y7bGh(I`P`{ zchEzhPTs7wPwH=#HcFS8U4-flarCPOOv&=ALuSyG$H_G**lykUbgJ(H9hNu|iXb>C zj5h}RzIb<_JBilgOjsd8V&aa|@=5<@b*JKRqk3bHn6!X${g@x+?CLUhPB#3@vS568KV?F zY6noC=ddpHZ7z0KU+`VrjU3bHt5Y0}9Ti2Dj!(mDkh|jPj{F90U_VqF3SmNFSDR~g zALpMbDG)8qabAB)Omt`FEEe)B$wqc08aAfauv81`T1N2r16o4>oM)!?S!dxmTMg1! zo-9+kI`$TLZg#u4D6=$FH#6jKZcj|ATrvA5EA!|<#v`x{U%92=$C4G@oR+pF!uaf z8YoJIdme|mw=XGc(SKHwF$fHaBOt~Cr0xIHy?mU=IW@e zsEu1she6LtJRXmrzywUo-W?a#IHo5Mj&765rQz&rXk_`Ikt>hxX5cd`p&^v_7ma{) zv%MhSRR>=}1~HMx+^Bj9rSVJ~$Go{xUgLv0kI&MulF|o_b+jC7SL1}fQ;V0!Q7ES0 zwC3KSYYWAYM&erh{G#w#Q4RlCRrA~M!QQusSyQYm^ZAnx}T;!zV^rX->a&T4z_#52;K?CMFF;AhP@wOzna$KgB{v0$|lH^+D>2h~T`2!074epo=vf>_>taj5%@Hng`l{Fb0{EBCctMi0Q96@esE=RW5pNbdxKn=}^yJMH>p4;UoKW6#Zzct(N^2 zUzaZ zCkic!wa-Tl1@~o^J{@7Z?D&pZ_tjA$*imig5!h37+&nUI)C3jiQ7vXq7u>Ja3_sAA z;Bbd6`%?80HVZ5KG_N>$b|-=n*vel}x-MVK;lB-VQSMq=5grY7%8Za}T(A6Gz#7$) z6G$MBCbnqdl570Cr?#(6x;uT}*}ilH_wDX*pMBCC(7oLJz4VKx*PNA*>ej!d57JaA zJ5Jr&zv|n3Zsi-_0PPaL3ibB*Ks~gK=S!@BW=7zb-Bz{IrEAe@yFT8F279_gV>P8Y zi;48BRT)Uh%~tbkzN++NyBXgTgO<{Z+=svAITi%P81X`-8F%J*$nbB#s=AoH{%YVhKaJ9~KXU8(d&Tya2x%}FE z(?(uNJo(1r+V@jwC&^bNQfuDn*AXhM;~Gqt7jLOPc~PYIMGQvgmb6rr*YcK#&ALG# zUakGkEsj@zG12Rd8^lp2UKDSph%BY|#=^GxS}!xjgo;U{r#zXo;7uzY!>7ovij)^; znf}I2zRPA_^CFKot~JcbXClkwgUE9=&i80ttomCe&F|Y=Hk}Ej(S`2Cw(}y5?pG+W;=Nj+fm@g!CZN^s!wRR z(!xzK{N_GwVcRj^o-;1VnloxS%O}PB@imXz<`XJz%Bu10^IB_t zVxoRjA)=_jSmc^|tWSRAkI$&mtZQbt5YQZ3|fyr)g)900M!m$UgaqM;8(at13A znUR(8@hod020Ve|dYkr5w_ndiOIk+%O6t3cMP)r??ASZ^>05J#tW0L1i)2v5;@x-&yCicycQKjf4X^z4aSn^ZCirav}AC_)0)s*#7vz1;R$baf2d?F<{4#I8E6jZF&lz}ApyzC&_t?z6wcu6#Vfhm3 zs!Z7`{_R!+TqwGuV1BEx7+*4%IRyH4E*N)G1qXJ3s88zdHlNp!Sr=&IMGU44_TYz} z3==YE5-T56&Db+H!Oj{euP_+`#4Hp?CQhGFIU;3j&1v~=#DC(j$~VwGBH}XMN_WJg zPXH1uxpxcTGQ+8=GGhF6|83s%&nzxAK7jX(J=mFX`M8O1V}AH`x{(XPX#A`1Sl^aQ zF~{~B?DBF+VUFkSbDhQE_oBv6d^b^*o!9Js~!XB@Mp%+L*k8txiJQD3hvwda=9ufSfG*;`6HN9ZL{be17Bl^ zOTLJkPwXKs-(#oMG29x1y~-gz?^ytF07{_)fiPfPXm zANmZO@}5(p;C|W;$zb7^+cIyFgUB5xZ{&&JpG@=kU=!g9K_BW zU&)vmpyZS%h=s_(Gvy5d!68Brj6=ru8^?`pEWFe333+b0q0a16q_kUXq(t+vjOn@h zygce7!N5A~t^&wM?t7qJGRO@!8KyA>n}=nD2mS$UeHz7W#*lYWZ#VLlhD8MPEL`2& z)DR~#H%&WOj^fG(mZNx_t3ztOD6S$vEUmWovK&_reC4(J*Qjm-!%kJ3)V^GxvmVfX z2{!5g;Mj13NrQc6Kg4N9ta*6F zdqY9W{HWa*Eq`r(r0O2!yo~GWX?eYLEkf4DqQ9Rg=#wrUo}IYoyNh$P7*fh@br2nB zB!X}MaxZp`eZf37TQB_iIjdZu--PSTPfE*W20Ope$7|UOcqE`FFFBP=c`Sb4y9Y&C zcEg6cKvQFF=^kqWBzRvkb{&34gmzIg?fW46#p1H%_NyEoK7!FWwZCZ(UdzA zR-M)Gs+jyCU2}oj@c_~SI{?jT@QX6mLNEh;Q^qzql3ovguXjzbB^;3?W@ov1?`bMy za8&HeJX9@#E}Q=Y_*!xO&hA`njqhd;DCLaiX6#Ke30-F$7}E`6&CK1uEf}nGWUvCc zSAge4of9jl=|;I1PA?i|8ZTwt@WGfwO47*hLo^g)gq+IoF}AZ%PwBly2c2;Qie{$d zGg6YYT$*k+T=TUg;_Nj`#CK66>NDKbk;i+5^A$Ko{^o z5a?2$e_VwS9s8{;C5eHTq4rst#={EuE7HoA%aZpOQiC9Vn%>>fV=i z^s1h@WyIv8p>r0SvOFuQ0qQRnS1hc{-gbKr4W+7fMka>K+1Lxnb!ljiHKZ})#@BE0 zmu0+CBOg_Pq&l23)B=24zjInPE4kFzvTdGs=27r=t1rgpT0v<-R*dv?5T$v1-Figf z2RgvJa0acR{!2n!-Kn7c5kGVsTSv!qZ@tXy9vh^)Vg zb7p_Hy@6%7M)~#NjE)Amw!0?fzW1HpgZ|% zA>AhkXWM3*=Zap4b62jqnurAlvVFR}d@WPHN8d6|AiT^xUPauQi*2v>E@6m!Zq=_T z^k+8;$=*EVy4!i{kO^Ghq*OjEidM+UIXKoZao>_yRgyqSA8LG3AV*Y-`gJrK0(F2$ zqGaHUQ7zo2VQ_Y&fVwcWaMj;~laZkAV>@DA9~TRb!NmBv`ESnlt2^JsCEgu+>M^)c zP7~;QmB25@t)e4!(#Ueo6f#zb+UY*N_919P)&#FY;va?2L*Sq}TPFhk$-|g9W#J2O z+kHqsw3KyddpO(XC*hjIUa%|W!BFVPbIQ4wbY)&bMY+Dd;)`P+-W4Ch~9u7*fE zl22J~BW3-0g@lRX*ep#TLyV}mXZ53WMtO6OjNac3CaSbU20vt)+WwWB9EW**;A@j_9yf6 z@q1=xCtM5|iWSIc6&@P`s7#Hk9~b3X6@gT!q5Hi!RxXm!Pqie@$+BmTW!7InD~^#{ z?Jc{u1g~5s2VL|dUhvxA*I)ecdIj$SXyRWvlPs(9HvKf#FU&?=B}%q7JWh7;Z&c%V z;@o#~C$B(hnU)@SM-MzNzd--qDCu4LN_sN4)$z5G+>}Z!LBXs@vg(bj<(zp$iCnO} zmb&%WFXvi(Vwjh@nIk1!4uV zDzar`3rER058r+2TVnL|(Q~446j`$B3lgN9Ej_$$Pb}uVTgbL*6{JW_6kO2+yPJ0~ z?-HNy*$1WMQ8igC&gDPzH%WegB~|qh8lzpvvZ%mbVDqDo*|?JBn7{L{P(wHSOUY!T zlTk;@10Hug%+S-WRneJ^5>9S?%b^n~F73GaBW_w-uU$?to)^S>(#kLN(4k}7I=62< z6d#&gplE)&4cLiFd!-}S3W;l6YbL@g?!Z02`wU;2f1g5z%O#ccr2^KzmC!dMCR>$9 zrZVWmA6peyH@o*b;}yT$;a7{Z7W&>~5XC31C}V3;sOC&jU*8oD337VrJylppZ~yL> z4z%Nv*qm>%z;tDPECMgOe-H^PKzjDzAW5z7Jl}hBo?QfV`mY=b-dgAzsbo5n8;KoN z`&$5kU$7QL7{j;uEg$yZxB?Wbz9P-u6f6m&^U8xI0o8UKs|BQzq+X%Xupg)(v8`!= zG8e{>Z%z!;U+4}JrJrv9E=!2&6pDoM$(P z#MTTQh~wc<;gK3&G>T~V-mn9gt?x{km_u`_niIFh8rSUpP~<9!>QYrqNCmPdSnJc4 zsLtm`9sn1zfpF%u{1!^K5@pH2c6Ch&;S(a*_4LGX zkE?L6sy(+4Jn|m->3-Sa9QCgd{#R$_vB8_L{H|jqE7)^)Wl^b5QVU~JjUpFa5) z1HX1lDW?n z&oz3PN2AppOfs&Z>)G)35*;f2rG(;s*dAwzc$^Ghv4F{-Lx-Nj9wXF%_GM5@kHtkQ zy;*#VZ)xn*)9Dz3^=ohXF;4=K+^^6CMkmXIqkwHHSUHR@!N0kgv3j_me1}VZaK$4M zpaPvHs^HPnW(V3@=>C3BBf8gol5g7+>*)KovJ`$D(jL5n(s1LyFpR3*%pgzgbt+6_;VaSLK{?~e=Z2P+*R-6@MKYBEN{&THlYOK8K(vbBD zuD1{Vb8Bk+pU=OHut8KbZ{RN7Kl=9O*(*oxOn^?Tvo!~AxYeqeSD^#50za^>35~!B z5cbtw!+&o2aN^I9|N6y;6L7gB`?QYuh0)(j{I_ra zkLwzX`~)0zG#@tmUmt5C>wsEFi*bYE|2)Y5Vcn*KLog=wjQ{VC1w5b@e0|l& { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +}; + +class DeviceStatusChangeListenerProxy : public IRemoteProxy { +public: + explicit DeviceStatusChangeListenerProxy(const sptr &impl); + ~DeviceStatusChangeListenerProxy() = default; + void OnChange(const DeviceInfo &results, const DeviceChangeType &type) override; +private: + static inline BrokerDelegator delegator_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // DEV_IDEVICE_STATUS_CHANGE_LISTENER_H \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore.h new file mode 100644 index 000000000..2821dee8e --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_KVSTORE_H +#define I_KVSTORE_H + +#include "message_parcel.h" +#include "iremote_broker.h" +#include "ikvstore_observer.h" +#include "ikvstore_snapshot.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class IKvStoreImpl : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreImpl") + + virtual void GetKvStoreSnapshot(sptr observer, + std::function)> callback) = 0; + + virtual Status ReleaseKvStoreSnapshot(sptr iKvStoreSnapshot) = 0; + + virtual Status Put(const Key &key, const Value &value) = 0; + + virtual Status PutBatch(const std::vector &entries) = 0; + + virtual Status Delete(const Key &key) = 0; + + virtual Status DeleteBatch(const std::vector &keys) = 0; + + virtual Status Clear() = 0; + + virtual Status StartTransaction() = 0; + + virtual Status Commit() = 0; + + virtual Status Rollback() = 0; + + virtual Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer) = 0; + + virtual Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) = 0; +}; + +class KvStoreImplStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; + +private: + int32_t GetKvStoreSnapshotOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t ReleaseKvStoreSnapshotOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t PutOnRemoteRequest(MessageParcel &data, MessageParcel &reply); + int32_t PutBatchOnRemoteRequest(MessageParcel &data, MessageParcel &reply); + int32_t DeleteOnRemoteRequest(MessageParcel &data, MessageParcel &reply); + int32_t DeleteBatchOnRemoteRequest(MessageParcel &data, MessageParcel &reply); + int32_t SubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t UnSubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); +}; + +class KvStoreImplProxy : public IRemoteProxy { +public: + explicit KvStoreImplProxy(const sptr &impl); + ~KvStoreImplProxy() = default; + virtual void GetKvStoreSnapshot(sptr observer, + std::function)> callback); + virtual Status ReleaseKvStoreSnapshot(sptr iKvStoreSnapshot); + virtual Status Put(const Key &key, const Value &value); + virtual Status PutBatch(const std::vector &entries); + virtual Status Delete(const Key &key); + virtual Status DeleteBatch(const std::vector &keys); + virtual Status Clear(); + virtual Status StartTransaction(); + virtual Status Commit(); + virtual Status Rollback(); + virtual Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer); + virtual Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer); +private: + static inline BrokerDelegator delegator_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_client_death_observer.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_client_death_observer.h new file mode 100644 index 000000000..15a83f10d --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_client_death_observer.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 I_KVSTORE_CLIENT_DEATH_OBSERVER_H +#define I_KVSTORE_CLIENT_DEATH_OBSERVER_H + +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" + +namespace OHOS { +namespace DistributedKv { + +class IKvStoreClientDeathObserver : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreClientDeathObserver"); +}; + +class KvStoreClientDeathObserverStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +}; + +class KvStoreClientDeathObserverProxy : public IRemoteProxy { +public: + explicit KvStoreClientDeathObserverProxy(const sptr &impl); + ~KvStoreClientDeathObserverProxy() = default; +private: + static inline BrokerDelegator delegator_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_CLIENT_DEATH_OBSERVER_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_data_service.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_data_service.h new file mode 100755 index 000000000..3755e1f0f --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_data_service.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 I_KV_STORE_DATA_SERVICE_H +#define I_KV_STORE_DATA_SERVICE_H + +#include "iremote_broker.h" +#include "ikvstore.h" +#include "ikvstore_client_death_observer.h" +#include "ikvstore_observer.h" +#include "ikvstore_single.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "message_parcel.h" +#include "types.h" +#include "idevice_status_change_listener.h" + +namespace OHOS { +namespace DistributedKv { +/* + * IPC-friendly Options struct without std::string schema field. + * Passing a struct with an std::string field is a potential security exploit. + * + */ +struct OptionsIpc { + bool createIfMissing; + bool encrypt; + bool persistant; + bool backup; + bool autoSync; + int securityLevel; + SyncPolicy syncPolicy; + KvStoreType kvStoreType; + bool syncable; // let bms delete first + bool dataOwnership; // true indicates the ownership of distributed data is DEVICE, otherwise, ACCOUNT +}; + +class IKvStoreDataService : public IRemoteBroker { +public: + enum { + GETKVSTORE, + GETALLKVSTOREID, + CLOSEKVSTORE, + CLOSEALLKVSTORE, + DELETEKVSTORE, + DELETEALLKVSTORE, + REGISTERCLIENTDEATHOBSERVER, + GETSINGLEKVSTORE, + GETLOCALDEVICE, + GETDEVICELIST, + STARTWATCHDEVICECHANGE, + STOPWATCHDEVICECHANGE, + SERVICE_CMD_LAST, + DATAUSAGESTART = 20, + DATAUSAGEEND = 40, + }; + + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreDataService"); + /* create and open kv store instance. */ + virtual Status GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) = 0; + + virtual Status GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) = 0; + + /* get all kv store names */ + virtual void GetAllKvStoreId(const AppId &appId, std::function &)> callback) = 0; + + /* open kv store instance will not receive subscribe any more. */ + virtual Status CloseKvStore(const AppId &appId, const StoreId &id) = 0; + + /* close all kvstore. */ + virtual Status CloseAllKvStore(const AppId &appId) = 0; + + /* delete kv store */ + virtual Status DeleteKvStore(const AppId &appId, const StoreId &id) = 0; + + /* delete kv store */ + virtual Status DeleteAllKvStore(const AppId &appId) = 0; + + virtual Status RegisterClientDeathObserver(const AppId &appId, sptr observer) = 0; + + virtual Status GetLocalDevice(DeviceInfo &device) = 0; + virtual Status GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) = 0; + virtual Status StartWatchDeviceChange(sptr observer, + DeviceFilterStrategy strategy) = 0; + virtual Status StopWatchDeviceChange(sptr observer) = 0; +}; + +class KvStoreDataServiceStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +private: + int32_t GetKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetAllKvStoreIdOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t CloseKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t CloseAllKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t DeleteKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t DeleteAllKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t RegisterClientDeathObserverOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetLocalDeviceOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetDeviceListOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t StartWatchDeviceChangeOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t StopWatchDeviceChangeOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetSingleKvStoreOnRemote(MessageParcel &data, MessageParcel &reply); + + using RequestHandler = int32_t(KvStoreDataServiceStub::*)(MessageParcel&, MessageParcel&); + static constexpr RequestHandler HANDLERS[SERVICE_CMD_LAST] = { + [GETKVSTORE] = &KvStoreDataServiceStub::GetKvStoreOnRemote, + [GETALLKVSTOREID] = &KvStoreDataServiceStub::GetAllKvStoreIdOnRemote, + [CLOSEKVSTORE] = &KvStoreDataServiceStub::CloseKvStoreOnRemote, + [CLOSEALLKVSTORE] = &KvStoreDataServiceStub::CloseAllKvStoreOnRemote, + [DELETEKVSTORE] = &KvStoreDataServiceStub::DeleteKvStoreOnRemote, + [DELETEALLKVSTORE] = &KvStoreDataServiceStub::DeleteAllKvStoreOnRemote, + [REGISTERCLIENTDEATHOBSERVER] = &KvStoreDataServiceStub::RegisterClientDeathObserverOnRemote, + [GETSINGLEKVSTORE] = &KvStoreDataServiceStub::GetSingleKvStoreOnRemote, + [GETLOCALDEVICE] = &KvStoreDataServiceStub::GetLocalDeviceOnRemote, + [GETDEVICELIST] = &KvStoreDataServiceStub::GetDeviceListOnRemote, + [STARTWATCHDEVICECHANGE] = &KvStoreDataServiceStub::StartWatchDeviceChangeOnRemote, + [STOPWATCHDEVICECHANGE] = &KvStoreDataServiceStub::StopWatchDeviceChangeOnRemote, + }; +}; + +class KvStoreDataServiceProxy : public IRemoteProxy { +public: + explicit KvStoreDataServiceProxy(const sptr &impl); + ~KvStoreDataServiceProxy() = default; + + /* create and open kv store instance. */ + virtual Status GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback); + + virtual Status GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback); + + /* get all kv store names */ + virtual void GetAllKvStoreId(const AppId &appId, std::function &)> callback); + + /* open kv store instance will not receive subscribe any more. */ + virtual Status CloseKvStore(const AppId &appId, const StoreId &storeId); + + /* close all kvstore. */ + virtual Status CloseAllKvStore(const AppId &appId); + + /* delete kv store */ + virtual Status DeleteKvStore(const AppId &appId, const StoreId &id); + + /* delete kv store */ + virtual Status DeleteAllKvStore(const AppId &appId); + + virtual Status RegisterClientDeathObserver(const AppId &appId, sptr observer); + + virtual Status GetLocalDevice(DeviceInfo &device); + virtual Status GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy); + virtual Status StartWatchDeviceChange(sptr observer, DeviceFilterStrategy strategy); + virtual Status StopWatchDeviceChange(sptr observer); +private: + static inline BrokerDelegator delegator_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KV_STORE_DATA_SERVICE_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_observer.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_observer.h new file mode 100644 index 000000000..b04d53ced --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_observer.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 I_KVSTORE_OBSERVER_H +#define I_KVSTORE_OBSERVER_H + +#include "change_notification.h" +#include "iremote_broker.h" +#include "ikvstore_observer.h" +#include "ikvstore_snapshot.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" + +namespace OHOS { +namespace DistributedKv { + +class IKvStoreObserver : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreObserver"); + virtual void OnChange(const ChangeNotification &changeNotification, sptr snapshot) = 0; +}; + +class KvStoreObserverStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +}; + +class KvStoreObserverProxy : public IRemoteProxy { +public: + explicit KvStoreObserverProxy(const sptr &impl); + ~KvStoreObserverProxy() = default; + void OnChange(const ChangeNotification &changeNotification, sptr snapshot) override; +private: + static inline BrokerDelegator delegator_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_OBSERVER_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_resultset.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_resultset.h new file mode 100644 index 000000000..ed707c30f --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_resultset.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 I_KVSTORE_RESULTSET_H +#define I_KVSTORE_RESULTSET_H + +#include "message_parcel.h" +#include "iremote_broker.h" +#include "ikvstore_observer.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" + +namespace OHOS::DistributedKv { +class IKvStoreResultSet : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreResultSet") + virtual int GetCount() = 0; + + virtual int GetPosition() = 0; + + virtual bool MoveToFirst() = 0; + + virtual bool MoveToLast() = 0; + + virtual bool MoveToNext() = 0; + + virtual bool MoveToPrevious() = 0; + + virtual bool Move(int offset) = 0; + + virtual bool MoveToPosition(int position) = 0; + + virtual bool IsFirst() = 0; + + virtual bool IsLast() = 0; + + virtual bool IsBeforeFirst() = 0; + + virtual bool IsAfterLast() = 0; + + virtual Status GetEntry(Entry &entry) = 0; +}; + +class KvStoreResultSetStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; + +private: + int GetEntryOnRemote(MessageParcel &reply); +}; + +class KvStoreResultSetProxy : public IRemoteProxy { +public: + explicit KvStoreResultSetProxy(const sptr &impl); + ~KvStoreResultSetProxy() = default; + virtual int GetCount(); + + virtual int GetPosition(); + + virtual bool MoveToFirst(); + + virtual bool MoveToLast(); + + virtual bool MoveToNext(); + + virtual bool MoveToPrevious(); + + virtual bool Move(int offset); + + virtual bool MoveToPosition(int position); + + virtual bool IsFirst(); + + virtual bool IsLast(); + + virtual bool IsBeforeFirst(); + + virtual bool IsAfterLast(); + + virtual Status GetEntry(Entry &entry); +private: + virtual int SendRequest(uint32_t code); + virtual bool SendRequestRetBool(uint32_t code); + static inline BrokerDelegator delegator_; +}; +} // namespace OHOS::DistributedKv +#endif // I_KVSTORE_RESULTSET_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_single.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_single.h new file mode 100755 index 000000000..20a1c15be --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_single.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_SINGLE_KVSTORE_H +#define I_SINGLE_KVSTORE_H + +#include + +#include "message_parcel.h" +#include "iremote_broker.h" +#include "ikvstore_observer.h" +#include "ikvstore_sync_callback.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" +#include "ikvstore_resultset.h" + +namespace OHOS { +namespace DistributedKv { +class ISingleKvStore : public IRemoteBroker { +public: + enum { + PUT, + DELETE, + GET, + SUBSCRIBEKVSTORE, + UNSUBSCRIBEKVSTORE, + GETENTRIES, + GETRESULTSET, + CLOSERESULTSET, + REMOVEDEVICEDATA, + SYNC, + REGISTERSYNCCALLBACK, + UNREGISTERSYNCCALLBACK, + PUTBATCH, + DELETEBATCH, + STARTTRANSACTION, + COMMIT, + ROLLBACK, + GETENTRIESWITHQUERY, + GETRESULTSETWITHQUERY, + GETCOUNTWITHQUERY, + CONTROL, + SETCAPABILITYENABLED, + SETCAPABILITYRANGE, + SETSECURITLEVEL, + SINGLE_CMD_LAST, + }; + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.ISingleKvStore") + virtual Status Put(const Key &key, const Value &value) = 0; + virtual Status Delete(const Key &key) = 0; + virtual Status Get(const Key &key, Value &value) = 0; + virtual Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer) = 0; + virtual Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) = 0; + virtual Status GetEntries(const Key &prefixKey, std::vector &entries) = 0; + virtual Status GetEntriesWithQuery(const std::string &query, std::vector &entries) = 0; + virtual void GetResultSet(const Key &prefixKey, + std::function)> callback) = 0; + virtual void GetResultSetWithQuery(const std::string &query, + std::function)> callback) = 0; + virtual Status CloseResultSet(sptr resultSet) = 0; + virtual Status GetCountWithQuery(const std::string &query, int &result) = 0; + virtual Status Sync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t allowedDelayMs) = 0; + virtual Status RemoveDeviceData(const std::string &device) = 0; + virtual Status RegisterSyncCallback(sptr callback) = 0; + virtual Status UnRegisterSyncCallback() = 0; + virtual Status PutBatch(const std::vector &entries) = 0; + virtual Status DeleteBatch(const std::vector &keys) = 0; + virtual Status StartTransaction() = 0; + virtual Status Commit() = 0; + virtual Status Rollback() = 0; + virtual Status Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) = 0; + virtual Status SetCapabilityEnabled(bool enabled) = 0; + virtual Status SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) = 0; + virtual Status GetSecurityLevel(SecurityLevel &securityLevel) = 0; +}; + +class SingleKvStoreStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +private: + int PutOnRemote(MessageParcel& data, MessageParcel& reply); + int DeleteOnRemote(MessageParcel& data, MessageParcel& reply); + int GetOnRemote(MessageParcel& data, MessageParcel& reply); + int SubscribeKvStoreOnRemote(MessageParcel& data, MessageParcel& reply); + int UnSubscribeKvStoreOnRemote(MessageParcel& data, MessageParcel& reply); + int GetEntriesOnRemote(MessageParcel& data, MessageParcel& reply); + int GetEntriesWithQueryOnRemote(MessageParcel& data, MessageParcel& reply); + int SyncOnRemote(MessageParcel& data, MessageParcel& reply); + int GetResultSetOnRemote(MessageParcel& data, MessageParcel& reply); + int GetResultSetWithQueryOnRemote(MessageParcel& data, MessageParcel& reply); + int GetCountWithQueryOnRemote(MessageParcel& data, MessageParcel& reply); + int CloseResultSetOnRemote(MessageParcel& data, MessageParcel& reply); + int RemoveDeviceDataOnRemote(MessageParcel& data, MessageParcel& reply); + int RegisterSyncCallbackOnRemote(MessageParcel& data, MessageParcel& reply); + int UnRegisterSyncCallbackOnRemote(MessageParcel& data, MessageParcel& reply); + int PutBatchOnRemote(MessageParcel& data, MessageParcel& reply); + int DeleteBatchOnRemote(MessageParcel& data, MessageParcel& reply); + int StartTransactionOnRemote(MessageParcel& data, MessageParcel& reply); + int CommitOnRemote(MessageParcel& data, MessageParcel& reply); + int RollbackOnRemote(MessageParcel& data, MessageParcel& reply); + int ControlOnRemote(MessageParcel& data, MessageParcel& reply); + int OnCapabilityEnableRequest(MessageParcel& data, MessageParcel& reply); + int OnCapabilityRangeRequest(MessageParcel& data, MessageParcel& reply); + int OnSecurityLevelRequest(MessageParcel& data, MessageParcel& reply); + + int WriteEntriesParcelable(MessageParcel& reply, Status status, std::vector entries, int bufferSize); + int GetTotalEntriesSize(std::vector entries); + + using RequestHandler = int(SingleKvStoreStub::*)(MessageParcel&, MessageParcel&); + static constexpr RequestHandler HANDLERS[SINGLE_CMD_LAST] = { + [PUT] = &SingleKvStoreStub::PutOnRemote, + [DELETE] = &SingleKvStoreStub::DeleteOnRemote, + [GET] = &SingleKvStoreStub::GetOnRemote, + [SUBSCRIBEKVSTORE] = &SingleKvStoreStub::SubscribeKvStoreOnRemote, + [UNSUBSCRIBEKVSTORE] = &SingleKvStoreStub::UnSubscribeKvStoreOnRemote, + [GETENTRIES] = &SingleKvStoreStub::GetEntriesOnRemote, + [GETRESULTSET] = &SingleKvStoreStub::GetResultSetOnRemote, + [CLOSERESULTSET] = &SingleKvStoreStub::CloseResultSetOnRemote, + [REMOVEDEVICEDATA] = &SingleKvStoreStub::RemoveDeviceDataOnRemote, + [SYNC] = &SingleKvStoreStub::SyncOnRemote, + [REGISTERSYNCCALLBACK] = &SingleKvStoreStub::RegisterSyncCallbackOnRemote, + [UNREGISTERSYNCCALLBACK] = &SingleKvStoreStub::UnRegisterSyncCallbackOnRemote, + [PUTBATCH] = &SingleKvStoreStub::PutBatchOnRemote, + [DELETEBATCH] = &SingleKvStoreStub::DeleteBatchOnRemote, + [STARTTRANSACTION] = &SingleKvStoreStub::StartTransactionOnRemote, + [COMMIT] = &SingleKvStoreStub::CommitOnRemote, + [ROLLBACK] = &SingleKvStoreStub::RollbackOnRemote, + [GETENTRIESWITHQUERY] = &SingleKvStoreStub::GetEntriesWithQueryOnRemote, + [GETRESULTSETWITHQUERY] = &SingleKvStoreStub::GetResultSetWithQueryOnRemote, + [GETCOUNTWITHQUERY] = &SingleKvStoreStub::GetCountWithQueryOnRemote, + [CONTROL] = &SingleKvStoreStub::ControlOnRemote, + [SETCAPABILITYENABLED] = &SingleKvStoreStub::OnCapabilityEnableRequest, + [SETCAPABILITYRANGE] = &SingleKvStoreStub::OnCapabilityRangeRequest, + [SETSECURITLEVEL] = &SingleKvStoreStub::OnSecurityLevelRequest, + }; +}; + +class SingleKvStoreProxy : public IRemoteProxy { +public: + explicit SingleKvStoreProxy(const sptr &impl); + ~SingleKvStoreProxy() = default; + virtual Status Put(const Key &key, const Value &value); + virtual Status Delete(const Key &key); + virtual Status Get(const Key &key, Value &value); + virtual Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer); + virtual Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer); + virtual Status GetEntries(const Key &prefixKey, std::vector &entries); + virtual Status GetEntriesWithQuery(const std::string &query, std::vector &entries); + virtual void GetResultSet(const Key &prefixKey, std::function)> callback); + virtual void GetResultSetWithQuery(const std::string &query, + std::function)> callback); + virtual Status CloseResultSet(sptr resultSet); + virtual Status GetCountWithQuery(const std::string &query, int &result); + virtual Status Sync(const std::vector &deviceIdList, const SyncMode &mode, uint32_t allowedDelayMs); + virtual Status RemoveDeviceData(const std::string &device); + virtual Status RegisterSyncCallback(sptr callback); + virtual Status UnRegisterSyncCallback(); + virtual Status PutBatch(const std::vector &entries); + virtual Status DeleteBatch(const std::vector &keys); + virtual Status StartTransaction(); + virtual Status Commit(); + virtual Status Rollback(); + virtual Status Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output); + virtual Status SetCapabilityEnabled(bool enabled); + virtual Status SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels); + virtual Status GetSecurityLevel(SecurityLevel &securityLevel); +private: + static inline BrokerDelegator delegator_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_SINGLE_KVSTORE_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_snapshot.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_snapshot.h new file mode 100644 index 000000000..843d16358 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_snapshot.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 I_KVSTORE_SNAPSHOT_H +#define I_KVSTORE_SNAPSHOT_H + +#include "message_parcel.h" +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class IKvStoreSnapshotImpl : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreSnapshotImpl") + virtual void GetEntries( + const Key &prefixKey, const Key &nextKey, + std::function &, + const OHOS::DistributedKv::Key &)> + callback) = 0; + + virtual void GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) = 0; + + virtual Status Get(const Key &key, Value &value) = 0; +}; + +class KvStoreSnapshotImplStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; + +private: + int32_t GetEntriesOnRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetKeysRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetRemote(MessageParcel &data, MessageParcel &reply); + int32_t GetTotalEntriesSize(std::vector entryList); + int32_t WriteEntriesParcelable(MessageParcel &reply, Status statusTmp, + std::vector entryList, int bufferSize, Key nxtKey); + int32_t GetTotalkeysSize(std::vector keyList); + int32_t WritekeysParcelable(MessageParcel &reply, Status statusTmp, + std::vector keyList, int bufferSize, Key nxtKey); +}; + +class KvStoreSnapshotImplProxy : public IRemoteProxy { +public: + explicit KvStoreSnapshotImplProxy(const sptr &impl); + virtual void GetEntries( + const Key &prefixKey, const Key &nextKey, + std::function &, + const OHOS::DistributedKv::Key &)> + callback); + + virtual void GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback); + + virtual Status Get(const Key &key, Value &value); + + virtual ~KvStoreSnapshotImplProxy(); +private: + static inline BrokerDelegator delegator_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_SNAPSHOT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_sync_callback.h b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_sync_callback.h new file mode 100644 index 000000000..285509698 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/ikvstore_sync_callback.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_KVSTORE_SYNC_CALLBACK_H +#define I_KVSTORE_SYNC_CALLBACK_H + +#include +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class IKvStoreSyncCallback : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.DistributedKv.IKvStoreSyncCallback"); + virtual void SyncCompleted(const std::map &results) = 0; +}; + +class KvStoreSyncCallbackStub : public IRemoteStub { +public: + virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; +}; + +class KvStoreSyncCallbackProxy : public IRemoteProxy { +public: + explicit KvStoreSyncCallbackProxy(const sptr &impl); + ~KvStoreSyncCallbackProxy() = default; + void SyncCompleted(const std::map &results) override; +private: + static inline BrokerDelegator delegator_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // I_KVSTORE_SYNC_CALLBACK_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/include/inner_types.h b/frameworks/innerkitsimpl/distributeddatafwk/include/inner_types.h new file mode 100644 index 000000000..add36329c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/include/inner_types.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 INNER_TYPES_H +#define INNER_TYPES_H + +namespace OHOS { +namespace DistributedKv { + +enum class InnerStatus { + SUCCESS = 0, + DECREASE_REFCOUNT, + ERROR, +}; + +} +} +#endif diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_blob.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_blob.cpp new file mode 100755 index 000000000..8a0058af5 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_blob.cpp @@ -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. + */ + +#include "app_blob.h" +#include +namespace OHOS { +namespace AppDistributedKv { +AppBlob::AppBlob(const AppBlob &blob) +{ + blob_ = blob.Data(); +} + +AppBlob::AppBlob(AppBlob &&blob) noexcept +{ + blob_.swap(blob.blob_); +} + +AppBlob &AppBlob::operator=(const AppBlob &blob) +{ + // Self-assignment detection + if (&blob == this) { + return *this; + } + + blob_ = blob.Data(); + return *this; +} + +AppBlob &AppBlob::operator=(AppBlob &&blob) noexcept +{ + // Self-assignment detection + if (&blob == this) { + return *this; + } + + blob_.swap(blob.blob_); + + return *this; +} + +AppBlob::AppBlob(const char *str, size_t n) + : blob_() +{ + if (str != nullptr) { + blob_ = std::vector(str, str + n); + } +} + +AppBlob::AppBlob(const std::string &str) + : blob_(str.begin(), str.end()) +{ +} + +AppBlob::AppBlob(const char *str) + : blob_() +{ + if (str != nullptr) { + blob_ = std::vector(str, str + strlen(str)); + } +} + +AppBlob::AppBlob(const std::vector &bytes) + : blob_(bytes) +{ +} + +const std::vector &AppBlob::Data() const +{ + return blob_; +} + +size_t AppBlob::Size() const +{ + return blob_.size(); +} + +bool AppBlob::Empty() const +{ + return (blob_.empty()); +} + +bool AppBlob::operator==(const AppBlob &blob) const +{ + return blob_ == blob.blob_; +} + +std::string AppBlob::ToString() const +{ + std::string str(blob_.begin(), blob_.end()); + return str; +} + +int AppBlob::Compare(const AppBlob &blob) const +{ + if (blob_ < blob.blob_) { + return -1; + } + if (blob_ == blob.blob_) { + return 0; + } + return 1; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_change_notification.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_change_notification.cpp new file mode 100755 index 000000000..35c76fcc6 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_change_notification.cpp @@ -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. + */ + +#include "app_change_notification.h" + +namespace OHOS { +namespace AppDistributedKv { +AppChangeNotification::AppChangeNotification() +{} + +AppChangeNotification::AppChangeNotification(const std::list &insertEntries, + const std::list &updateEntries, + const std::list &deleteEntries, + const std::string &deviceId, + const bool isClear) + : insertEntries_(insertEntries), updateEntries_(updateEntries), deleteEntries_(deleteEntries), + deviceId_(deviceId), isClear_(isClear) +{} + +AppChangeNotification::~AppChangeNotification() +{} + +const std::list &AppChangeNotification::GetInsertEntries() const +{ + return this->insertEntries_; +} + +const std::list &AppChangeNotification::GetUpdateEntries() const +{ + return this->updateEntries_; +} + +const std::list &AppChangeNotification::GetDeleteEntries() const +{ + return this->deleteEntries_; +} + +const std::string &AppChangeNotification::GetDeviceId() const +{ + return this->deviceId_; +} + +bool AppChangeNotification::IsClear() const +{ + return this->isClear_; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager.cpp new file mode 100755 index 000000000..5c6a70c58 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager.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. + */ + +#include "app_distributed_kv_data_manager.h" +#include "app_distributed_kv_data_manager_impl.h" + +namespace OHOS { +namespace AppDistributedKv { +std::shared_ptr AppDistributedKvDataManager::GetInstance(const std::string &bundleName, + const std::string &dataDir, + const std::string &userId) +{ + return AppDistributedKvDataManagerImpl::GetInstance(bundleName, dataDir, userId); +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.cpp new file mode 100755 index 000000000..1ffeafd8c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AppDistributedKvDataManagerImpl" + +#include "app_distributed_kv_data_manager_impl.h" +#include +#include +#include +#include +#include "app_kvstore_impl.h" +#include "communication_provider.h" +#include "constant.h" +#include "types.h" +#include "log_print.h" +#include "reporter.h" +#include "account_delegate.h" +#include "delegate_mgr_callback.h" +#include "process_communicator_impl.h" +#include "kvstore_utils.h" +namespace OHOS { +namespace AppDistributedKv { +using namespace OHOS::DistributedKv; +const std::string DATABASE_DIR = "distributeddb"; +const std::string DEVICE_COLLABORATION_ABBRE = "_DDC"; + +std::mutex AppDistributedKvDataManagerImpl::storeMutex_; +std::map> AppDistributedKvDataManagerImpl::managers_; + +std::shared_ptr AppDistributedKvDataManagerImpl::GetInstance(const std::string &bundleName, + const std::string &dataDir, + const std::string &userId) +{ + ZLOGI("start"); + std::lock_guard lck(storeMutex_); + auto it = managers_.find(bundleName + userId); + if (it != managers_.end()) { + return it->second; + } + + std::string tempDataDir = dataDir; + ZLOGD("tempDataDir : %s", tempDataDir.c_str()); + if (!ForceCreateDirectory(tempDataDir)) { + ZLOGE("create directories %s failed", tempDataDir.c_str()); + FaultMsg msg = {FaultType::SERVICE_FAULT, "device", "GetInstance", Fault::SF_CREATE_DIR}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + return nullptr; + } + // default mode is 0755 + if (!ChangeModeDirectory(tempDataDir, DistributedKv::Constant::DEFAULT_MODE)) { + return nullptr; + } + + std::string appId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (appId.empty()) { + appId = bundleName; + } + auto status = DistributedDB::KvStoreDelegateManager::SetProcessLabel(appId + DEVICE_COLLABORATION_ABBRE, userId); + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate SetProcessLabel failed: %d.", static_cast(status)); + FaultMsg msg = {FaultType::SERVICE_FAULT, "device", "GetInstance", Fault::SF_PROCESS_LABEL}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + return nullptr; + } + + auto communicator = std::make_shared(); + auto commStatus = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + if (commStatus != DistributedDB::DBStatus::OK) { + ZLOGW("set distributed db communicator failed."); + return nullptr; + } + + auto *delegateManager = new(std::nothrow) DistributedDB::KvStoreDelegateManager(appId, userId); + if (delegateManager == nullptr) { + ZLOGW("new kvStoredelegateManager failed"); + return nullptr; + } + DistributedDB::KvStoreConfig kvStoreConfig { tempDataDir }; + status = delegateManager->SetKvStoreConfig(kvStoreConfig); + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate SetKvStoreConfig failed: %d.", static_cast(status)); + FaultMsg msg = {FaultType::SERVICE_FAULT, "device", "GetInstance", Fault::SF_DATABASE_CONFIG}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + delete delegateManager; + delegateManager = nullptr; + return nullptr; + } + + auto temp = std::make_shared(delegateManager, appId); + if (temp == nullptr) { + delete delegateManager; + delegateManager = nullptr; + ZLOGW("new AppDistributedKvDataManagerImpl failed"); + return nullptr; + } + + managers_.insert({bundleName + userId, temp}); + ZLOGD("return a new managerSingleton_."); + return temp; +} + +AppDistributedKvDataManagerImpl::AppDistributedKvDataManagerImpl( + DistributedDB::KvStoreDelegateManager *delegateManager, const std::string &appId) + : kvStoreDelegateManager_(delegateManager), appId_(appId) +{ + ZLOGI("construct"); +} + +AppDistributedKvDataManagerImpl::~AppDistributedKvDataManagerImpl() +{ + ZLOGI("destruct"); + delete kvStoreDelegateManager_; +} + +Status AppDistributedKvDataManagerImpl::GetKvStore( + const Options &options, const std::string &storeId, + const std::function appKvStore)> &callback) +{ + ZLOGI("start."); + std::string trimmedStoreId = DistributedKv::Constant::TrimCopy(storeId); + if (trimmedStoreId.size() == 0 || trimmedStoreId.size() > DistributedKv::Constant::MAX_STORE_ID_LENGTH) { + ZLOGE("storeId is invalid."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreDelegateManager_ == nullptr) { + ZLOGE("kvStoreDelegateManager_ is nullptr."); + return Status::ILLEGAL_STATE; + } + Status status = Status::ERROR; + DistributedDB::KvStoreNbDelegate::Option dbOption; + dbOption.createIfNecessary = options.createIfMissing; + dbOption.isMemoryDb = !options.persistant; + dbOption.secOption = ConvertSecurityLevel(options.securityLevel); + kvStoreDelegateManager_->GetKvStore( + trimmedStoreId, dbOption, + [&](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) { + if (dbStatus == DistributedDB::DBStatus::OK && kvStoreNbDelegate != nullptr) { + status = Status::SUCCESS; + callback(std::make_unique(trimmedStoreId, kvStoreNbDelegate)); + ZLOGI("succeed."); + auto statDelegateMgr = std::make_shared(kvStoreDelegateManager_); + auto statDelegate = std::static_pointer_cast(statDelegateMgr); + Reporter::GetInstance()->DatabaseStatistic()->Report( + {AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), appId_, storeId, 0, statDelegate}); + return; + } + + status = AppKvStoreImpl::ConvertErrorCode(dbStatus); + if (kvStoreNbDelegate == nullptr) { + status = Status::STORE_NOT_FOUND; + } + ZLOGW("appKvStore return nullptr."); + callback(nullptr); + FaultMsg msg = {FaultType::RUNTIME_FAULT, ModuleName::DEVICE, "GetKvStore", Fault::RF_GET_DB}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + }); + + ZLOGI("get status: %d.", static_cast(status)); + return status; +} + +Status AppDistributedKvDataManagerImpl::CloseKvStore(std::unique_ptr appKvStore) +{ + ZLOGI("start."); + if (appKvStore == nullptr) { + ZLOGE("appKvStore is nullptr."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreDelegateManager_ == nullptr) { + ZLOGE("kvStoreDelegateManager_ is nullptr."); + return Status::ILLEGAL_STATE; + } + return reinterpret_cast(appKvStore.get())->Close(kvStoreDelegateManager_); +} + +Status AppDistributedKvDataManagerImpl::DeleteKvStore(const std::string &storeId) +{ + ZLOGI("start."); + if (kvStoreDelegateManager_ == nullptr) { + ZLOGE("kvStoreDelegateManager_ is nullptr."); + return Status::ILLEGAL_STATE; + } + std::string trimmedStoreId = DistributedKv::Constant::TrimCopy(storeId); + if (trimmedStoreId.empty() || trimmedStoreId.size() > DistributedKv::Constant::MAX_STORE_ID_LENGTH) { + ZLOGW("invalid storeId."); + return Status::INVALID_ARGUMENT; + } + DistributedDB::DBStatus status = kvStoreDelegateManager_->DeleteKvStore(trimmedStoreId); + if (status == DistributedDB::DBStatus::OK) { + ZLOGI("delete KVStore succeed."); + return Status::SUCCESS; + } + ZLOGE("delete KVStore failed."); + return Status::ERROR; +} + +Status AppDistributedKvDataManagerImpl::GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) +{ + ZLOGI("start"); + if (kvStoreDelegateManager_ == nullptr) { + ZLOGE("kvStoreDelegateManager_ is nullptr."); + return Status::ILLEGAL_STATE; + } + DistributedDB::DBStatus status = kvStoreDelegateManager_->GetKvStoreDiskSize(storeId, size); + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("Failed to getStoreDiskSize, storeID: %s", storeId.c_str()); + return Status::ERROR; + } + ZLOGI("end, size:%" PRIu64, size); + return Status::SUCCESS; +} + +Status AppDistributedKvDataManagerImpl::RegisterKvStoreCorruptionObserver( + const std::shared_ptr observer) +{ + ZLOGI("start"); + if (observer == nullptr) { + ZLOGE("observer is nullptr."); + return Status::INVALID_ARGUMENT; + } + if (corruptionObserver_ != nullptr) { + return Status::REPEATED_REGISTER; + } + if (kvStoreDelegateManager_ == nullptr) { + return Status::ERROR; + } + corruptionObserver_ = observer; + kvStoreDelegateManager_->SetKvStoreCorruptionHandler([&](const std::string& appId, const std::string& userId, + const std::string& storeId) { + corruptionObserver_->OnCorruption(appId, userId, storeId); + }); + ZLOGD("end"); + return Status::SUCCESS; +} + +DistributedDB::SecurityOption AppDistributedKvDataManagerImpl::ConvertSecurityLevel(int securityLevel) +{ + if (securityLevel < SecurityLevel::NO_LABEL || securityLevel > SecurityLevel::S4) { + return {SecurityLevel::NO_LABEL, DistributedDB::ECE}; + } + switch (securityLevel) { + case SecurityLevel::S3: + return {DistributedDB::S3, DistributedDB::SECE}; + case SecurityLevel::S4: + return {DistributedDB::S4, DistributedDB::ECE}; + default: + return {securityLevel, DistributedDB::ECE}; + } +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.h new file mode 100755 index 000000000..9032b7a85 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.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 APP_DISTRIBUTED_KV_DATA_MANAGER_IMPL_H +#define APP_DISTRIBUTED_KV_DATA_MANAGER_IMPL_H + +#include +#include "app_device_status_change_listener.h" +#include "app_distributed_kv_data_manager.h" +#include "app_kvstore.h" +#include "app_types.h" +#include "kv_store_delegate_manager.h" +#include "process_communicator_impl.h" + +namespace OHOS { +namespace AppDistributedKv { +// This is the overall manager of all kvstore. +// This class provides open, close, delete AppKvStore and manage remote device functions. +class AppDistributedKvDataManagerImpl final : public AppDistributedKvDataManager { +public: + AppDistributedKvDataManagerImpl(DistributedDB::KvStoreDelegateManager *delegateManager, const std::string &appId); + + static std::shared_ptr GetInstance(const std::string &bundleName, + const std::string &dataDir, + const std::string &userId); + + virtual ~AppDistributedKvDataManagerImpl(); + + Status GetKvStore(const Options &options, const std::string &storeId, + const std::function appKvStore)> &callback) override; + + Status CloseKvStore(std::unique_ptr appKvStore) override; + + Status DeleteKvStore(const std::string &storeId) override; + + Status GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) override; + + Status RegisterKvStoreCorruptionObserver(const std::shared_ptr observer) override; +private: + static DistributedDB::SecurityOption ConvertSecurityLevel(int securityLevel); + + static std::mutex storeMutex_; + + static std::map> managers_; + // pointer of class DistributedDB::KvStoreDelegateManager. defined as void* to avoid exposing inside implementions. + DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager_; + std::string appId_; + + std::shared_ptr corruptionObserver_; +}; // class AppDistributedKvDataManagerImpl +} // namespace AppDistributedKv +} // namespace OHOS +#endif // APP_DISTRIBUTED_KV_DATA_MANAGER_IMPL_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.cpp new file mode 100755 index 000000000..c8582e891 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.cpp @@ -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. + */ + +#define LOG_TAG "AppKvStoreConflictDataImpl" + +#include "app_kvstore_conflict_data_impl.h" +#include "log_print.h" + +namespace OHOS { +namespace AppDistributedKv { +AppKvStoreConflictDataImpl::AppKvStoreConflictDataImpl(const KvStoreConflictEntry &kvStoreConflictEntry) + : kvStoreConflictEntry_(kvStoreConflictEntry) +{ + ZLOGI("constructor"); +} + +AppKvStoreConflictDataImpl::~AppKvStoreConflictDataImpl() +{ + ZLOGI("destructor"); +} + +AppKvStoreConflictPolicyType AppKvStoreConflictDataImpl::GetType() const +{ + return static_cast(kvStoreConflictEntry_.type); +} + +void AppKvStoreConflictDataImpl::GetKey(Key &key) const +{ + key = kvStoreConflictEntry_.key; +} + +Status AppKvStoreConflictDataImpl::GetValue(ConflictValueType type, Value &value) const +{ + if (type == ConflictValueType::OLD_VALUE) { + if (!kvStoreConflictEntry_.oldData.isDeleted) { + value = kvStoreConflictEntry_.oldData.value; + } + return kvStoreConflictEntry_.oldData.status; + } else { + if (!kvStoreConflictEntry_.newData.isDeleted) { + value = kvStoreConflictEntry_.newData.value; + } + return kvStoreConflictEntry_.newData.status; + } +} + +bool AppKvStoreConflictDataImpl::IsDeleted(ConflictValueType type) const +{ + if (type == ConflictValueType::OLD_VALUE) { + return kvStoreConflictEntry_.oldData.isDeleted; + } + return kvStoreConflictEntry_.newData.isDeleted; +} + +bool AppKvStoreConflictDataImpl::IsNative(ConflictValueType type) const +{ + if (type == ConflictValueType::OLD_VALUE) { + return kvStoreConflictEntry_.oldData.isLocal; + } else { + return kvStoreConflictEntry_.newData.isLocal; + } +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.h new file mode 100644 index 000000000..75c91de52 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.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 APP_KVSTORE_CONFLICT_DATA_IMPL_H +#define APP_KVSTORE_CONFLICT_DATA_IMPL_H + +#include "app_kvstore_conflict_data.h" +#include "app_types.h" +#include "kv_store_nb_conflict_data.h" + +namespace OHOS { +namespace AppDistributedKv { +struct KvStoreConflictData { + Value value {}; + bool isDeleted = false; + bool isLocal = false; + Status status {}; +}; + +struct KvStoreConflictEntry { + int type = 1; + Key key {}; + KvStoreConflictData oldData {}; + KvStoreConflictData newData {}; +}; + +class AppKvStoreConflictDataImpl final : public AppKvStoreConflictData { +public: + explicit AppKvStoreConflictDataImpl(const KvStoreConflictEntry &kvStoreConflictEntry); + + ~AppKvStoreConflictDataImpl(); + + AppKvStoreConflictPolicyType GetType() const override; + + void GetKey(Key &key) const override; + + Status GetValue(ConflictValueType type, Value &value) const override; + + bool IsDeleted(ConflictValueType type) const override; + + bool IsNative(ConflictValueType type) const override; + +private: + const KvStoreConflictEntry kvStoreConflictEntry_; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_KVSTORE_CONFLICT_DATA_IMPL_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.cpp new file mode 100755 index 000000000..3af4e2ded --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.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. + */ + +#define LOG_TAG "AppKvStoreImpl" + +#include "app_kvstore_impl.h" +#include +#include +#include +#include "app_kvstore_result_set_impl.h" +#include "app_types.h" +#include "log_print.h" +#include "types.h" +#include "reporter.h" + +namespace OHOS { +namespace AppDistributedKv { +using namespace std::chrono_literals; +using namespace OHOS::DistributedKv; +AppKvStoreImpl::AppKvStoreImpl(const std::string &storeId, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) + : storeId_(storeId), kvStoreNbDelegate_(kvStoreNbDelegate) +{ + ZLOGI("constructor appkvstore"); +} + +AppKvStoreImpl::~AppKvStoreImpl() +{ + ZLOGI("destructor appKvStore."); + // This function will be called after DB close, so every Subscribe/Unsubscribe call will fail and no more element + // will be added to observer maps. + { + std::lock_guard localLock(localObserverMapMutex_); + for (auto const &iter : localObserverMap_) { + delete iter.second; + } + localObserverMap_.clear(); + } + { + std::lock_guard syncLock(syncedObserverMapMutex_); + for (auto const &iter : syncedObserverMap_) { + delete iter.second; + } + syncedObserverMap_.clear(); + } +} + +// Get id of this AppKvStore. +const std::string &AppKvStoreImpl::GetStoreId() +{ + return storeId_; +} + +// options: Mark this is a local entry or not. +Status AppKvStoreImpl::Put(const WriteOptions &options, const Key &key, const Value &value) +{ + ZLOGD("start"); + auto trimmedKey = DistributedKv::Constant::TrimCopy>(key.Data()); + // Restrict key and value size to interface specification. + if (trimmedKey.size() == 0 || trimmedKey.size() > DistributedKv::Constant::MAX_KEY_LENGTH || + value.Data().size() > DistributedKv::Constant::MAX_VALUE_LENGTH) { + ZLOGW("invalid_argument."); + return Status::INVALID_ARGUMENT; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::Value tmpValue = value.Data(); + DistributedDB::DBStatus status; + if (options.local) { + status = kvStoreNbDelegate_->PutLocal(tmpKey, tmpValue); + } else { + status = kvStoreNbDelegate_->Put(tmpKey, tmpValue); + } + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("put succeed."); + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + return Status::SUCCESS; + } + ZLOGW("put failed. status: %d.", static_cast(status)); + return ConvertErrorCode(status); +} + +// options: mark this delete is a local change or not. +Status AppKvStoreImpl::Delete(const WriteOptions &options, const Key &key) +{ + ZLOGD("start."); + auto trimmedKey = DistributedKv::Constant::TrimCopy>(key.Data()); + if (trimmedKey.size() == 0 || trimmedKey.size() > DistributedKv::Constant::MAX_KEY_LENGTH) { + ZLOGW("invalid argument."); + return Status::INVALID_ARGUMENT; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::DBStatus status; + if (options.local) { + status = kvStoreNbDelegate_->DeleteLocal(tmpKey); + } else { + status = kvStoreNbDelegate_->Delete(tmpKey); + } + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("succeed."); + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + return Status::SUCCESS; + } + ZLOGW("failed status: %d.", static_cast(status)); + return ConvertErrorCode(status); +} + +// Get value from AppKvStore by its key. Set options->local to true if you want to get from local kvstore. +// Parameters: +// options: mark we get from local store or remote store. options->batch is a reserved parameter and should +// always be false. +Status AppKvStoreImpl::Get(const ReadOptions &options, const Key &key, Value &value) +{ + ZLOGD("start"); + auto trimmedKey = DistributedKv::Constant::TrimCopy>(key.Data()); + if (trimmedKey.size() == 0 || trimmedKey.size() > DistributedKv::Constant::MAX_KEY_LENGTH) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::Value tmpValue; + DistributedDB::DBStatus status; + if (options.local) { + status = kvStoreNbDelegate_->GetLocal(tmpKey, tmpValue); + } else { + status = kvStoreNbDelegate_->Get(tmpKey, tmpValue); + } + ZLOGD("status: %d.", static_cast(status)); + // Value don't have other write method. + Value tmpValueForCopy(tmpValue); + value = tmpValueForCopy; + if (status == DistributedDB::DBStatus::OK) { + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + return Status::SUCCESS; + } + return ConvertErrorCode(status); +} + +// Get all entries in this store which key start with prefixKey. +// Parameters: +// options: mark we get from local store or remote store. options->batch is a reserved parameter and should +// always be false. +Status AppKvStoreImpl::GetEntries(const Key &prefixKey, std::vector &entries) +{ + ZLOGD("start."); + auto trimmedPrefix = DistributedKv::Constant::TrimCopy>(prefixKey.Data()); + if (trimmedPrefix.size() > DistributedKv::Constant::MAX_KEY_LENGTH) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::Key tmpKeyPrefix = trimmedPrefix; + std::vector dbEntries; + DistributedDB::DBStatus status = kvStoreNbDelegate_->GetEntries(tmpKeyPrefix, dbEntries); + if (status == DistributedDB::DBStatus::OK) { + entries.reserve(dbEntries.size()); + ZLOGD("vector size: %zu status: %d.", dbEntries.size(), static_cast(status)); + for (auto const &dbEntry : dbEntries) { + Key tmpKey(dbEntry.key); + Value tmpValue(dbEntry.value); + entries.push_back({.key = tmpKey, .value = tmpValue}); + } + return Status::SUCCESS; + } + return ConvertErrorCode(status); +} + +// Get all entries in this store which key start with prefixKey. +Status AppKvStoreImpl::GetEntries(const Key &prefixKey, AppKvStoreResultSet *&resultSet) +{ + ZLOGD("start."); + auto trimmedPrefix = DistributedKv::Constant::TrimCopy>(prefixKey.Data()); + if (trimmedPrefix.size() > DistributedKv::Constant::MAX_KEY_LENGTH) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::Key tmpKeyPrefix = trimmedPrefix; + DistributedDB::KvStoreResultSet *dbResultSet = nullptr; + DistributedDB::DBStatus status = kvStoreNbDelegate_->GetEntries(tmpKeyPrefix, dbResultSet); + if (status == DistributedDB::DBStatus::OK) { + resultSet = new (std::nothrow) AppKvStoreResultSetImpl(dbResultSet, kvStoreNbDelegate_); + if (resultSet == nullptr) { + ZLOGW("new resultSet failed."); + return Status::ERROR; + } + return Status::SUCCESS; + } + return ConvertErrorCode(status); +} + +// Close the result set returned by GetEntries(). +Status AppKvStoreImpl::CloseResultSet(AppKvStoreResultSet *&resultSet) +{ + if (resultSet == nullptr) { + return Status::INVALID_ARGUMENT; + } + if (resultSet->Close() == Status::SUCCESS) { + delete resultSet; + resultSet = nullptr; + return Status::SUCCESS; + } + return Status::ERROR; +} + +// Close this kvstore in KvStoreDelegateManager. This method is called before this store object destruct. +Status AppKvStoreImpl::Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager) +{ + if (kvStoreDelegateManager == nullptr) { + return Status::INVALID_ARGUMENT; + } + + DistributedDB::DBStatus status = kvStoreDelegateManager->CloseKvStore(kvStoreNbDelegate_); + if (status == DistributedDB::DBStatus::OK) { + VisitStat vs { appId_, __FUNCTION__ }; + Reporter::GetInstance()->VisitStatistic()->Report(vs); + return Status::SUCCESS; + } + return Status::ERROR; +} + +// Sync store with other devices. This is an asynchronous method, +// sync will fail if there is a syncing operation in progress. +// Parameters: +// deviceIdList: device list to sync. +// mode: mode can be set to SyncMode::PUSH, SyncMode::PULL and SyncMode::PUTH_PULL. PUSH_PULL will firstly +// push all not-local store to listed devices, then pull these stores back. +// callback: return map to caller. +// Return: +// Status of this Sync operation. +Status AppKvStoreImpl::Sync(const std::vector &deviceIdList, const SyncMode &mode, + const std::function &)> &callback) +{ + ZLOGD("start."); + DistributedDB::DBStatus status; + DistributedDB::SyncMode dbMode; + if (mode == SyncMode::PUSH) { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PUSH_ONLY; + } else if (mode == SyncMode::PULL) { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PULL_ONLY; + } else { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PUSH_PULL; + } + + bool syncStatus = syncStatus_.try_lock(); + if (!syncStatus) { + ZLOGW("Another sync operation still in progress, still call sync right now."); + } + status = kvStoreNbDelegate_->Sync(deviceIdList, dbMode, + [&, callback](const std::map &devices) { + syncStatus_.unlock(); + std::map resultMap; + for (auto device : devices) { + if (device.second == DistributedDB::DBStatus::OK) { + resultMap[device.first] = Status::SUCCESS; + } else if (device.second == DistributedDB::DBStatus::NOT_FOUND) { + resultMap[device.first] = Status::DEVICE_NOT_FOUND; + } else if (device.second == DistributedDB::DBStatus::TIME_OUT) { + resultMap[device.first] = Status::TIME_OUT; + } else { + resultMap[device.first] = Status::ERROR; + } + } + ZLOGD("callback."); + callback(resultMap); + }); + + ZLOGD("end: %d", static_cast(status)); + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + if (status == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + return ConvertErrorCode(status); +} + +// Register change of this kvstore to a client-defined observer. observer->OnChange method will be called when store +// changes. One observer can subscribe more than one AppKvStore. +// Parameters: +// options: mark this is a local entry or not. +// subscribeType: OBSERVER_CHANGES_NATIVE means native changes of syncable kv store, +// : OBSERVER_CHANGES_FOREIGN means synced data changes from remote devices, +// : OBSERVER_CHANGES_ALL means both native changes and synced data changes. +// observer: observer to subscribe changes. +// Return: +// Status of this subscribe operation. +Status AppKvStoreImpl::SubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) +{ + ZLOGD("start."); + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::DBStatus dbStatus; + KvStoreObserverNbImpl *nbObserver = new (std::nothrow) KvStoreObserverNbImpl(observer, subscribeType); + if (nbObserver == nullptr) { + ZLOGW("new KvStoreObserverNbImpl failed"); + return Status::ERROR; + } + DistributedDB::Key emptyKey; + if (options.local) { + std::lock_guard lock(localObserverMapMutex_); + bool alreadySubscribed = (localObserverMap_.find(observer) != localObserverMap_.end()); + if (alreadySubscribed) { + delete nbObserver; + return Status::STORE_ALREADY_SUBSCRIBE; + } + + dbStatus = kvStoreNbDelegate_->RegisterObserver( + emptyKey, DistributedDB::ObserverMode::OBSERVER_CHANGES_LOCAL_ONLY, nbObserver); + if (dbStatus == DistributedDB::DBStatus::OK) { + localObserverMap_.insert(std::pair(observer, nbObserver)); + } + } else { + std::lock_guard lock(syncedObserverMapMutex_); + bool alreadySubscribed = (syncedObserverMap_.find(observer) != syncedObserverMap_.end()); + if (alreadySubscribed) { + delete nbObserver; + return Status::STORE_ALREADY_SUBSCRIBE; + } + int dbObserverMode; + if (subscribeType == SubscribeType::OBSERVER_CHANGES_NATIVE) { + dbObserverMode = DistributedDB::ObserverMode::OBSERVER_CHANGES_NATIVE; + } else if (subscribeType == SubscribeType::OBSERVER_CHANGES_FOREIGN) { + dbObserverMode = DistributedDB::ObserverMode::OBSERVER_CHANGES_FOREIGN; + } else { + dbObserverMode = DistributedDB::ObserverMode::OBSERVER_CHANGES_FOREIGN | + DistributedDB::ObserverMode::OBSERVER_CHANGES_NATIVE; + } + dbStatus = kvStoreNbDelegate_->RegisterObserver(emptyKey, dbObserverMode, nbObserver); + if (dbStatus == DistributedDB::DBStatus::OK) { + syncedObserverMap_.insert(std::pair(observer, nbObserver)); + } + } + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + if (dbStatus == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + + delete nbObserver; + if (dbStatus == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + if (dbStatus == DistributedDB::DBStatus::DB_ERROR) { + return Status::DB_ERROR; + } + return Status::ERROR; +} + +// Unregister a kvstore to an observer. +// Parameters: +// options: mark this is a local entry or not. +// subscribeType: OBSERVER_CHANGES_NATIVE means native changes of syncable kv store, +// : OBSERVER_CHANGES_FOREIGN means synced data changes from remote devices, +// : OBSERVER_CHANGES_ALL means both native changes and synced data changes. +// observer: observer to unsubscribe this store. +// Return: +// Status of this unsubscribe operation. +Status AppKvStoreImpl::UnSubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) +{ + ZLOGD("start."); + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::DBStatus dbStatus; + if (options.local) { + std::lock_guard lock(localObserverMapMutex_); + auto nbObserver = localObserverMap_.find(observer); + if (nbObserver == localObserverMap_.end()) { + return Status::STORE_NOT_SUBSCRIBE; + } + dbStatus = kvStoreNbDelegate_->UnRegisterObserver(nbObserver->second); + if (dbStatus == DistributedDB::DBStatus::OK) { + delete nbObserver->second; + localObserverMap_.erase(nbObserver); + } + } else { + std::lock_guard lock(syncedObserverMapMutex_); + auto nbObserver = syncedObserverMap_.find(observer); + if (nbObserver == syncedObserverMap_.end()) { + return Status::STORE_NOT_SUBSCRIBE; + } + dbStatus = kvStoreNbDelegate_->UnRegisterObserver(nbObserver->second); + if (dbStatus == DistributedDB::DBStatus::OK) { + delete nbObserver->second; + syncedObserverMap_.erase(nbObserver); + } + } + Reporter::GetInstance()->VisitStatistic()->Report({appId_, __FUNCTION__}); + if (dbStatus == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + if (dbStatus == DistributedDB::DBStatus::NOT_FOUND) { + return Status::STORE_NOT_SUBSCRIBE; + } + if (dbStatus == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + return Status::ERROR; +} + +Status AppKvStoreImpl::RemoveDeviceData(const std::string &device) +{ + ZLOGD("start"); + DistributedDB::DBStatus status = kvStoreNbDelegate_->RemoveDeviceData(device); + if (status == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + return Status::ERROR; +} + +Status AppKvStoreImpl::SetConflictResolutionPolicy( + AppKvStoreConflictPolicyType appConflictPolicyType, + std::function callback) +{ + ZLOGD("start."); + int conflictType = static_cast(appConflictPolicyType); + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate_->SetConflictNotifier( + conflictType, [callback, this](const DistributedDB::KvStoreNbConflictData &kvStoreNbConflictData) { + ZLOGD("callback "); + KvStoreConflictEntry kvstoreConflictEntry; + FormKvStoreConflictEntry(kvStoreNbConflictData, kvstoreConflictEntry); + AppKvStoreConflictDataImpl appConflictDataImpl(kvstoreConflictEntry); + callback(appConflictDataImpl); + }); + if (dbStatus != DistributedDB::DBStatus::OK) { + return Status::DB_ERROR; + } + return Status::SUCCESS; +} + +void AppKvStoreImpl::FormKvStoreConflictEntry(const DistributedDB::KvStoreNbConflictData &data, + KvStoreConflictEntry &kvstoreConflictEntry) +{ + kvstoreConflictEntry.type = data.GetType(); + DistributedDB::Key dbKey; + data.GetKey(dbKey); + Key tmpKey(dbKey); + kvstoreConflictEntry.key = tmpKey; + FormKvStoreConflictData(data, DistributedDB::KvStoreNbConflictData::ValueType::OLD_VALUE, + kvstoreConflictEntry.oldData); + FormKvStoreConflictData(data, DistributedDB::KvStoreNbConflictData::ValueType::NEW_VALUE, + kvstoreConflictEntry.newData); +} + +void AppKvStoreImpl::FormKvStoreConflictData(const DistributedDB::KvStoreNbConflictData &data, + DistributedDB::KvStoreNbConflictData::ValueType type, + KvStoreConflictData &kvStoreConflictData) +{ + DistributedDB::DBStatus dbStatus; + kvStoreConflictData.isLocal = data.IsNative(type); + kvStoreConflictData.isDeleted = data.IsDeleted(type); + if (!kvStoreConflictData.isDeleted) { + DistributedDB::Value dbValue; + dbStatus = data.GetValue(type, dbValue); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Failed to handle conflict, error: bad conflict data"); + kvStoreConflictData.status = Status::DB_ERROR; + return; + } + Value tmpValue(dbValue); + kvStoreConflictData.status = Status::SUCCESS; + kvStoreConflictData.value = tmpValue; + } +} + +Status AppKvStoreImpl::Export(const std::string &filePath, const std::vector &passwd) +{ + ZLOGD("export start"); + if (filePath.empty()) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::CipherPassword password; + auto status = password.SetValue(passwd.data(), passwd.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate_->Export(filePath, password); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Failed to export, path:%s", filePath.c_str()); + return Status::DB_ERROR; + } + ZLOGD("export end"); + return Status::SUCCESS; +} + +Status AppKvStoreImpl::Import(const std::string &filePath, const std::vector &passwd) +{ + ZLOGD("Import start"); + if (filePath.empty()) { + return Status::INVALID_ARGUMENT; + } + DistributedDB::CipherPassword password; + auto status = password.SetValue(passwd.data(), passwd.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate_->Import(filePath, password); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Failed to export, path:%s", filePath.c_str()); + return Status::DB_ERROR; + } + ZLOGD("Import end"); + return Status::SUCCESS; +} + +Status AppKvStoreImpl::GetSecurityLevel(SecurityLevel &securityLevel) const +{ + ZLOGD("start"); + DistributedDB::SecurityOption option; + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate_->GetSecurityOption(option); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Failed to get security level"); + return Status::DB_ERROR; + } + switch (option.securityLabel) { + case DistributedDB::NOT_SET: + case DistributedDB::S0: + case DistributedDB::S1: + case DistributedDB::S2: + securityLevel = static_cast(option.securityLabel); + break; + case DistributedDB::S3: + securityLevel = option.securityFlag ? S3 : S3_EX; + break; + case DistributedDB::S4: + securityLevel = S4; + break; + default: + break; + } + ZLOGD("end"); + return Status::SUCCESS; +} + +Status AppKvStoreImpl::ConvertErrorCode(DistributedDB::DBStatus status) +{ + switch (status) { + case DistributedDB::DBStatus::BUSY: + case DistributedDB::DBStatus::DB_ERROR: + return Status::DB_ERROR; + case DistributedDB::DBStatus::NOT_FOUND: + return Status::KEY_NOT_FOUND; + case DistributedDB::DBStatus::INVALID_ARGS: + return Status::INVALID_ARGUMENT; + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: + break; + } + return Status::ERROR; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.h new file mode 100755 index 000000000..a9c73732c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.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 APP_KV_STORE_IMPL_H +#define APP_KV_STORE_IMPL_H + +#include +#include +#include "app_kvstore.h" +#include "app_kvstore_conflict_data_impl.h" +#include "app_kvstore_observer.h" +#include "app_kvstore_result_set.h" +#include "app_types.h" +#include "constant.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "kvstore_observer_nb_impl.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppKvStoreImpl : public AppKvStore { +public: + AppKvStoreImpl(const std::string &storeId, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + + AppKvStoreImpl(AppKvStoreImpl &&) = delete; + AppKvStoreImpl &operator=(AppKvStoreImpl &&) = delete; + + // forbidden copy constructor. + AppKvStoreImpl(const AppKvStoreImpl &) = delete; + AppKvStoreImpl &operator=(const AppKvStoreImpl &) = delete; + + virtual ~AppKvStoreImpl(); + + // Get id of this AppKvStore. + const std::string &GetStoreId() override; + + // Write a pair of key and value to this store. Set write option to local if you do not this entry sync to other + // devices. + // Parameters: + // options: mark this is a local entry or not. + // key: key of this entry. Should be less than 256 bytes. key will be trimmed before store. + // value: value of this entry. Should be less than (1024 * 1024) bytes. + // Return: + // Status of this put operation. + Status Put(const WriteOptions &options, const Key &key, const Value &value) override; + + // Delete an entry by its key. Set write option to local if you want this delete to be a local change. + // Parameters: + // options: mark this delete is a local change or not. + // key: key of the entry to be deleted. + // Return: + // Status of this delete operation. + Status Delete(const WriteOptions &options, const Key &key) override; + + // Get value from AppKvStore by its key. Set options->local to true if you want to get from local kvstore. + // Parameters: + // options: mark we get from local store or remote store. options->batch is a reserved parameter and should + // always be false. + // key: key of this entry. + // value: value will be returned in this parameter. + // Return: + // Status of this get operation. + Status Get(const ReadOptions &options, const Key &key, Value &value) override; + + // Get all entries in this store which key start with prefixKey. This function will always get from synced store. + // Parameters: + // prefixkey: the prefix to be searched. + // entries: entries will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + Status GetEntries(const Key &prefixKey, std::vector &entries) override; + + // Get all entries in this store which key start with prefixKey. This function will always get from synced store. + // Parameters: + // prefixkey: the prefix to be searched. + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + Status GetEntries(const Key &prefixKey, AppKvStoreResultSet *&resultSet) override; + + // Close the result set returned by GetEntries(). + // Parameters: + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + Status CloseResultSet(AppKvStoreResultSet *&resultSet) override; + + // Sync store with other devices. This is an asynchronous method, + // sync will fail if there is a syncing operation in progress. + // Parameters: + // deviceIdList: device list to sync. + // mode: mode can be set to SyncMode::PUSH, SyncMode::PULL and SyncMode::PUTH_PULL. PUSH_PULL will firstly + // push all not-local store to listed devices, then pull these stores back. + // callback: return map to caller. + // Return: + // Status of this Sync operation. + Status Sync(const std::vector &deviceIdList, const SyncMode &mode, + const std::function &)> &callback) override; + + // Register change of this kvstore to a client-defined observer. observer->OnChange method will be called when store + // changes. One observer can subscribe more than one AppKvStore. + // Parameters: + // options: mark this is a local entry or not. + // subscribeType: OBSERVER_CHANGES_NATIVE means native changes of syncable kv store, + // : OBSERVER_CHANGES_FOREIGN means synced data changes from remote devices, + // : OBSERVER_CHANGES_ALL means both native changes and synced data changes. + // observer: observer to subscribe changes. + // Return: + // Status of this subscribe operation. + Status SubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) override; + + // Unregister a kvstore to an observer. + // Parameters: + // options: mark this is a local entry or not. + // subscribeType: OBSERVER_CHANGES_NATIVE means native changes of syncable kv store, + // : OBSERVER_CHANGES_FOREIGN means synced data changes from remote devices, + // : OBSERVER_CHANGES_LOCAL_ONLY means local changes of local kv store. + // observer: observer to unsubscribe this store. + // Return: + // Status of this unsubscribe operation. + Status UnSubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) override; + + // Close this kvstore in KvStoreDelegateManager. This method is called before this store object destruct. + Status Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager); + + // Remove Devvice data when device offline. + // Parameters: + // device: device id. + // Return: + // Status of this remove operation. + Status RemoveDeviceData(const std::string &device) override; + + // Set policy of conflict resolution. + // Parameters: + // appConflictPolicyType: include CONFLICT_FOREIGN_KEY_ONLY CONFLICT_FOREIGN_KEY_ORIG CONFLICT_NATIVE_ALL. + // callback: conflict resolution callback. + // Return: + // Status of Setting policy operation. + Status SetConflictResolutionPolicy(AppKvStoreConflictPolicyType appConflictPolicyType, + std::function callback) override; + + Status Export(const std::string &filePath, const std::vector &passwd) override; + + Status Import(const std::string &filePath, const std::vector &passwd) override; + + Status GetSecurityLevel(SecurityLevel &securityLevel) const override; + + static Status ConvertErrorCode(DistributedDB::DBStatus status); +private: + void FormKvStoreConflictEntry(const DistributedDB::KvStoreNbConflictData &data, + KvStoreConflictEntry &kvstoreConflictEntry); + void FormKvStoreConflictData(const DistributedDB::KvStoreNbConflictData &data, + DistributedDB::KvStoreNbConflictData::ValueType type, + KvStoreConflictData &kvStoreConflictData); + // user account get from User Account System. + std::string userId_; + // appId get from PMS. + std::string appId_; + // kvstore name. + std::string storeId_; + std::mutex syncStatus_ {}; + + // distributeddb is responsible for free kvStoreNbDelegate_, + // (destruct will be done while calling CloseKvStore in KvStoreDelegateManager) + // so DO NOT free it in AppKvStoreImpl's destructor. + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate_ = nullptr; + + std::map syncedObserverMap_ {}; + std::mutex syncedObserverMapMutex_ {}; + std::map localObserverMap_ {}; + std::mutex localObserverMapMutex_ {}; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // APP_KV_STORE_IMPL_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.cpp new file mode 100755 index 000000000..0cecd9a0f --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.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. + */ + +#define LOG_TAG "AppKvStoreResultSetImpl" + +#include "app_kvstore_result_set_impl.h" +#include "app_types.h" + +namespace OHOS { +namespace AppDistributedKv { +const int AppKvStoreResultSetImpl::INIT_POSTION = -1; + +AppKvStoreResultSetImpl::AppKvStoreResultSetImpl(DistributedDB::KvStoreResultSet *resultSet, + DistributedDB::KvStoreNbDelegate *delegate) + : kvStoreResultSet_(resultSet), nbDelegate_(delegate) +{ +} + +AppKvStoreResultSetImpl::~AppKvStoreResultSetImpl() +{ +} + +// Returns the count of rows in the result set. +int AppKvStoreResultSetImpl::GetCount() const +{ + return (kvStoreResultSet_ == nullptr) ? 0 : kvStoreResultSet_->GetCount(); +} + +// Returns the current read position of the result set. +int AppKvStoreResultSetImpl::GetPosition() const +{ + return (kvStoreResultSet_ == nullptr) ? INIT_POSTION : kvStoreResultSet_->GetPosition(); +} + +// Move the read position to the first row, return false if the result set is empty. +bool AppKvStoreResultSetImpl::MoveToFirst() +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->MoveToFirst(); +} + +// Move the read position to the last row, return false if the result set is empty. +bool AppKvStoreResultSetImpl::MoveToLast() +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->MoveToLast(); +} + +// 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. +bool AppKvStoreResultSetImpl::MoveToNext() +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->MoveToNext(); +} + +// 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. +bool AppKvStoreResultSetImpl::MoveToPrevious() +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->MoveToPrevious(); +} + +// Move the read position by a relative amount from the current position. +bool AppKvStoreResultSetImpl::Move(int offset) +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->Move(offset); +} + +// Move the read position to an absolute position value. +bool AppKvStoreResultSetImpl::MoveToPosition(int position) +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->MoveToPosition(position); +} + +// Returns whether the read position is pointing to the first row. +bool AppKvStoreResultSetImpl::IsFirst() const +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->IsFirst(); +} + +// Returns whether the read position is pointing to the last row. +bool AppKvStoreResultSetImpl::IsLast() const +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->IsLast(); +} + +// Returns whether the read position is before the first row. +bool AppKvStoreResultSetImpl::IsBeforeFirst() const +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->IsBeforeFirst(); +} + +// Returns whether the read position is after the last row +bool AppKvStoreResultSetImpl::IsAfterLast() const +{ + return (kvStoreResultSet_ == nullptr) ? false : kvStoreResultSet_->IsAfterLast(); +} + +// Get a key-value entry. +Status AppKvStoreResultSetImpl::GetEntry(Entry &entry) const +{ + if (kvStoreResultSet_ == nullptr) { + return Status::ERROR; + } + if (GetCount() == 0) { + return Status::KEY_NOT_FOUND; + } + DistributedDB::Entry dbEntry; + DistributedDB::DBStatus dbStatus = kvStoreResultSet_->GetEntry(dbEntry); + if (dbStatus == DistributedDB::DBStatus::OK) { + Key tmpKey(dbEntry.key); + Value tmpValue(dbEntry.value); + entry.key = tmpKey; + entry.value = tmpValue; + return Status::SUCCESS; + } + return Status::KEY_NOT_FOUND; +} + +Status AppKvStoreResultSetImpl::Close() +{ + if (kvStoreResultSet_ == nullptr) { + return Status::SUCCESS; + } + + if (nbDelegate_ == nullptr) { + return Status::INVALID_ARGUMENT; + } + + auto result = nbDelegate_->CloseResultSet(kvStoreResultSet_); + if (result == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + kvStoreResultSet_ = nullptr; + return Status::ERROR; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.h new file mode 100755 index 000000000..e39eeda29 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.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 APP_KV_STORE_RESULT_SET_IMPL_H +#define APP_KV_STORE_RESULT_SET_IMPL_H + +#include "app_kvstore_result_set.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_result_set.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppKvStoreResultSetImpl : public AppKvStoreResultSet { +public: + AppKvStoreResultSetImpl(DistributedDB::KvStoreResultSet *resultSet, DistributedDB::KvStoreNbDelegate *delegate); + + ~AppKvStoreResultSetImpl(); + + // Returns the count of rows in the result set. + int GetCount() const override; + + // Returns the current read position of the result set. + int GetPosition() const override; + + // Move the read position to the first row, return false if the result set is empty. + bool MoveToFirst() override; + + // Move the read position to the last row, return false if the result set is empty. + bool MoveToLast() override; + + // 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. + bool MoveToNext() override; + + // Move the read position to the previous row, + // return false if result set is empty or the read position is already before the first entry in the result set. + bool MoveToPrevious() override; + + // Move the read position by a relative amount from the current position. + bool Move(int offset) override; + + // Move the read position to an absolute position value. + bool MoveToPosition(int position) override; + + // Returns whether the read position is pointing to the first row. + bool IsFirst() const override; + + // Returns whether the read position is pointing to the last row. + bool IsLast() const override; + + // Returns whether the read position is before the first row. + bool IsBeforeFirst() const override; + + // Returns whether the read position is after the last row + bool IsAfterLast() const override; + + // Get a key-value entry. + Status GetEntry(Entry &entry) const override; + + Status Close() override; +private: + DistributedDB::KvStoreResultSet *kvStoreResultSet_ = nullptr; + DistributedDB::KvStoreNbDelegate *nbDelegate_ = nullptr; + static const int INIT_POSTION; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // APP_KV_STORE_RESULT_SET_IMPL_H \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/blob.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/blob.cpp new file mode 100755 index 000000000..93d2c3470 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/blob.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Blob" + +#include "blob.h" +#include +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +Blob::Blob() { } + +Blob::Blob(const Blob &blob) +{ + blob_ = blob.Data(); +} + +Blob::Blob(Blob &&blob) +{ + blob_.swap(blob.blob_); +} + +Blob &Blob::operator=(const Blob &blob) +{ + // Self-assignment detection + if (&blob == this) { + return *this; + } + + blob_ = blob.Data(); + + return *this; +} + +Blob &Blob::operator=(Blob &&blob) +{ + // Self-assignment detection + if (&blob == this) { + return *this; + } + + blob_.swap(blob.blob_); + + return *this; +} + +Blob::Blob(const char *str, size_t n) + : blob_() +{ + if (str != nullptr) { + blob_ = std::vector(str, str + n); + } +} + +Blob::Blob(const std::string &str) + : blob_(str.begin(), str.end()) +{ +} + +Blob::Blob(const char *str) + : blob_() +{ + if (str != nullptr) { + blob_ = std::vector(str, str + strlen(str)); + } +} + +Blob::Blob(const std::vector &bytes) + : blob_(bytes) +{ +} + +Blob::Blob(std::vector &&bytes) + : blob_(std::move(bytes)) +{ +} + +const std::vector &Blob::Data() const +{ + return blob_; +} + +size_t Blob::Size() const +{ + return blob_.size(); +} + +int Blob::RawSize() const +{ + return sizeof(int) + blob_.size(); +} + +bool Blob::Empty() const +{ + return blob_.empty(); +} + +uint8_t Blob::operator[](size_t n) const +{ + if (n >= Size()) { + ZLOGE("Trying to get a out-of-range Blob member."); + return 0; + } + return blob_[n]; +} + +bool Blob::operator==(const Blob &blob) const +{ + return blob_ == blob.blob_; +} + +void Blob::Clear() +{ + blob_.clear(); +} + +std::string Blob::ToString() const +{ + std::string str(blob_.begin(), blob_.end()); + return str; +} + +int Blob::Compare(const Blob &blob) const +{ + if (blob_ < blob.blob_) { + return -1; + } + if (blob_ == blob.blob_) { + return 0; + } + return 1; +} + +bool Blob::StartsWith(const Blob &blob) const +{ + size_t len = blob.Size(); + if (Size() < len) { + return false; + } + + for (size_t i = 0; i < len; ++i) { + if (blob_[i] != blob.blob_[i]) { + return false; + } + } + return true; +} + +bool Blob::Marshalling(Parcel &parcel) const +{ + return parcel.WriteUInt8Vector(this->blob_); +} + +Blob *Blob::Unmarshalling(Parcel &parcel) +{ + std::vector blobData; + if (!parcel.ReadUInt8Vector(&blobData)) { + return nullptr; + } + return new Blob(blobData); +} + +/* write blob size and data to memory buffer. return error when bufferLeftSize not enough. */ +bool Blob::WriteToBuffer(uint8_t *&cursorPtr, int &bufferLeftSize) const +{ + if (cursorPtr == nullptr || bufferLeftSize < static_cast(blob_.size() + sizeof(int))) { + return false; + } + *reinterpret_cast(cursorPtr) = static_cast(blob_.size()); + bufferLeftSize -= sizeof(int32_t); + cursorPtr += sizeof(int32_t); + errno_t err = memcpy_s(cursorPtr, bufferLeftSize, blob_.data(), blob_.size()); + if (err != EOK) { + return false; + } + cursorPtr += blob_.size(); + bufferLeftSize -= blob_.size(); + return true; +} + +/* read a blob from memory buffer. */ +bool Blob::ReadFromBuffer(const uint8_t *&cursorPtr, int &bufferLeftSize) +{ + if (cursorPtr == nullptr || bufferLeftSize < static_cast(sizeof(int))) { + return false; + } + int blobSize = *reinterpret_cast(cursorPtr); + bufferLeftSize -= sizeof(int) + blobSize; + if (blobSize < 0 || bufferLeftSize < 0) { + return false; + } + cursorPtr += sizeof(int); + blob_ = std::vector(cursorPtr, cursorPtr + blobSize); + cursorPtr += blobSize; + return true; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/change_notification.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/change_notification.cpp new file mode 100644 index 000000000..a20d8151d --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/change_notification.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ChangeNotification" + +#include "change_notification.h" +#include "constant.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +ChangeNotification::ChangeNotification(const std::list &insertEntries, const std::list &updateEntries, + const std::list &deleteEntries, const std::string &deviceId, + const bool isClear) + : insertEntries_(insertEntries), updateEntries_(updateEntries), deleteEntries_(deleteEntries), + deviceId_(deviceId), isClear_(isClear) +{} + +ChangeNotification::~ChangeNotification() +{} + +const std::list &ChangeNotification::GetInsertEntries() const +{ + return this->insertEntries_; +} + +const std::list &ChangeNotification::GetUpdateEntries() const +{ + return this->updateEntries_; +} + +const std::list &ChangeNotification::GetDeleteEntries() const +{ + return this->deleteEntries_; +} + +const std::string &ChangeNotification::GetDeviceId() const +{ + return this->deviceId_; +} + +bool ChangeNotification::IsClear() const +{ + return this->isClear_; +} + +bool ChangeNotification::Marshalling(Parcel &parcel) const +{ + if (!parcel.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + return false; + } + int32_t lenInsert = static_cast(insertEntries_.size()); + if (!parcel.WriteInt32(lenInsert)) { + return false; + } + + for (const auto &entry : insertEntries_) { + if (!parcel.WriteParcelable(&entry)) { + return false; + } + } + + int32_t lenUpdate = static_cast(updateEntries_.size()); + if (!parcel.WriteInt32(lenUpdate)) { + return false; + } + for (const auto &entry : updateEntries_) { + if (!parcel.WriteParcelable(&entry)) { + return false; + } + } + + int32_t lenDelete = static_cast(deleteEntries_.size()); + if (!parcel.WriteInt32(lenDelete)) { + return false; + } + for (const auto &entry : deleteEntries_) { + if (!parcel.WriteParcelable(&entry)) { + return false; + } + } + if (!parcel.WriteString(deviceId_)) { + ZLOGE("WriteString deviceId_ failed."); + return false; + } + + return parcel.WriteBool(isClear_); +} + +ChangeNotification *ChangeNotification::Unmarshalling(Parcel &parcel) +{ + std::list insertEntries; + std::list updateEntries; + std::list deleteEntries; + + int lenInsert = parcel.ReadInt32(); + if (lenInsert < 0) { + ZLOGE("lenInsert is %d", lenInsert); + return nullptr; + } + for (int i = 0; i < lenInsert; i++) { + sptr entryTmp = parcel.ReadParcelable(); + if (entryTmp != nullptr) { + insertEntries.push_back(*entryTmp); + } else { + ZLOGE("insertEntries get nullptr"); + return nullptr; + } + } + + int lenUpdate = parcel.ReadInt32(); + if (lenUpdate < 0) { + ZLOGE("lenUpdate is %d", lenUpdate); + return nullptr; + } + for (int i = 0; i < lenUpdate; i++) { + sptr entryTmp = parcel.ReadParcelable(); + if (entryTmp != nullptr) { + updateEntries.push_back(*entryTmp); + } else { + ZLOGE("updateEntries get nullptr"); + return nullptr; + } + } + + int lenDelete = parcel.ReadInt32(); + if (lenDelete < 0) { + ZLOGE("lenDelete is %d", lenDelete); + return nullptr; + } + for (int i = 0; i < lenDelete; i++) { + sptr entryTmp = parcel.ReadParcelable(); + if (entryTmp != nullptr) { + deleteEntries.push_back(*entryTmp); + } else { + ZLOGE("deleteEntries get nullptr"); + return nullptr; + } + } + std::string deviceId = parcel.ReadString(); + bool isClear = parcel.ReadBool(); + ChangeNotification *changeNotification = + new ChangeNotification(insertEntries, updateEntries, deleteEntries, deviceId, isClear); + return changeNotification; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/data_query.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/data_query.cpp new file mode 100755 index 000000000..897a63bb4 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/data_query.cpp @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DataQuery" + +#include "data_query.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +const std::string DataQuery::EQUAL_TO = "^EQUAL"; +const std::string DataQuery::NOT_EQUAL_TO = "^NOT_EQUAL"; +const std::string DataQuery::GREATER_THAN = "^GREATER"; +const std::string DataQuery::LESS_THAN = "^LESS"; +const std::string DataQuery::GREATER_THAN_OR_EQUAL_TO = "^GREATER_EQUAL"; +const std::string DataQuery::LESS_THAN_OR_EQUAL_TO = "^LESS_EQUAL"; +const std::string DataQuery::IS_NULL = "^IS_NULL"; +const std::string DataQuery::IN = "^IN"; +const std::string DataQuery::NOT_IN = "^NOT_IN"; +const std::string DataQuery::LIKE = "^LIKE"; +const std::string DataQuery::NOT_LIKE = "^NOT_LIKE"; +const std::string DataQuery::AND = "^AND"; +const std::string DataQuery::OR = "^OR"; +const std::string DataQuery::ORDER_BY_ASC = "^ASC"; +const std::string DataQuery::ORDER_BY_DESC = "^DESC"; +const std::string DataQuery::LIMIT = "^LIMIT"; +const std::string DataQuery::SPACE = " "; +const std::string DataQuery::SPECIAL = "^"; +const std::string DataQuery::SPECIAL_ESCAPE = "(^)"; +const std::string DataQuery::SPACE_ESCAPE = "^^"; +const std::string DataQuery::EMPTY_STRING = "^EMPTY_STRING"; +const std::string DataQuery::START_IN = "^START"; +const std::string DataQuery::END_IN = "^END"; +const std::string DataQuery::BEGIN_GROUP = "^BEGIN_GROUP"; +const std::string DataQuery::END_GROUP = "^END_GROUP"; +const std::string DataQuery::KEY_PREFIX = "^KEY_PREFIX"; +const std::string DataQuery::DEVICE_ID = "^DEVICE_ID"; +const std::string DataQuery::IS_NOT_NULL = "^IS_NOT_NULL"; +const std::string DataQuery::TYPE_STRING = "STRING"; +const std::string DataQuery::TYPE_INTEGER = "INTEGER"; +const std::string DataQuery::TYPE_LONG = "LONG"; +const std::string DataQuery::TYPE_DOUBLE = "DOUBLE"; +const std::string DataQuery::TYPE_BOOLEAN = "BOOL"; +const std::string DataQuery::VALUE_TRUE = "true"; +const std::string DataQuery::VALUE_FALSE = "false"; +const std::string DataQuery::SUGGEST_INDEX = "^SUGGEST_INDEX"; +constexpr int MAX_QUERY_LENGTH = 5 * 1024; // Max query string length 5k + +DataQuery::DataQuery() +{} + +DataQuery& DataQuery::Reset() +{ + str_ = ""; + return *this; +} + +DataQuery& DataQuery::EqualTo(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(EQUAL_TO, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::EqualTo(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(EQUAL_TO, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::EqualTo(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(EQUAL_TO, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::EqualTo(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(EQUAL_TO, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::EqualTo(const std::string &field, const bool value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonBoolean(EQUAL_TO, TYPE_BOOLEAN, myField, value); + } + return *this; +} + +DataQuery& DataQuery::NotEqualTo(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(NOT_EQUAL_TO, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::NotEqualTo(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(NOT_EQUAL_TO, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::NotEqualTo(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(NOT_EQUAL_TO, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::NotEqualTo(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(NOT_EQUAL_TO, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::NotEqualTo(const std::string &field, const bool value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonBoolean(NOT_EQUAL_TO, TYPE_BOOLEAN, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThan(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThan(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThan(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThan(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(GREATER_THAN, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::LessThan(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThan(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThan(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThan(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(LESS_THAN, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::GreaterThanOrEqualTo(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN_OR_EQUAL_TO, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThanOrEqualTo(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN_OR_EQUAL_TO, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThanOrEqualTo(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(GREATER_THAN_OR_EQUAL_TO, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::GreaterThanOrEqualTo(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(GREATER_THAN_OR_EQUAL_TO, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::LessThanOrEqualTo(const std::string &field, const int value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN_OR_EQUAL_TO, TYPE_INTEGER, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThanOrEqualTo(const std::string &field, const int64_t value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN_OR_EQUAL_TO, TYPE_LONG, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThanOrEqualTo(const std::string &field, const double value) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommon(LESS_THAN_OR_EQUAL_TO, TYPE_DOUBLE, myField, value); + } + return *this; +} + +DataQuery& DataQuery::LessThanOrEqualTo(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(LESS_THAN_OR_EQUAL_TO, TYPE_STRING, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::IsNull(const std::string &field) +{ + std::string myField = field; + if (ValidateField(myField)) { + str_.append(SPACE); + str_.append(IS_NULL); + str_.append(SPACE); + EscapeSpace(myField); + str_.append(myField); + } + return *this; +} + +DataQuery& DataQuery::IsNotNull(const std::string &field) +{ + std::string myField = field; + if (ValidateField(myField)) { + str_.append(SPACE); + str_.append(IS_NOT_NULL); + str_.append(SPACE); + EscapeSpace(myField); + str_.append(myField); + } + return *this; +} + +DataQuery& DataQuery::InInt(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(IN, TYPE_INTEGER, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::InLong(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(IN, TYPE_LONG, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::InDouble(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(IN, TYPE_DOUBLE, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::InString(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + std::vector myValueList(valueList); + if (ValidateField(myField)) { + AppendCommonListString(IN, TYPE_STRING, myField, myValueList); + } + return *this; +} + +DataQuery& DataQuery::NotInInt(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(NOT_IN, TYPE_INTEGER, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::NotInLong(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(NOT_IN, TYPE_LONG, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::NotInDouble(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + if (ValidateField(myField)) { + AppendCommonList(NOT_IN, TYPE_DOUBLE, myField, valueList); + } + return *this; +} + +DataQuery& DataQuery::NotInString(const std::string &field, const std::vector &valueList) +{ + std::string myField = field; + std::vector myValueList(valueList); + if (ValidateField(myField)) { + AppendCommonListString(NOT_IN, TYPE_STRING, myField, myValueList); + } + return *this; +} + +DataQuery& DataQuery::Like(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(LIKE, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::Unlike(const std::string &field, const std::string &value) +{ + std::string myField = field; + std::string myValue = value; + if (ValidateField(myField)) { + AppendCommonString(NOT_LIKE, myField, myValue); + } + return *this; +} + +DataQuery& DataQuery::And() +{ + str_.append(SPACE); + str_.append(AND); + return *this; +} + +DataQuery& DataQuery::Or() +{ + str_.append(SPACE); + str_.append(OR); + return *this; +} + +DataQuery& DataQuery::OrderByAsc(const std::string &field) +{ + std::string myField = field; + if (ValidateField(myField)) { + str_.append(SPACE); + str_.append(ORDER_BY_ASC); + str_.append(SPACE); + EscapeSpace(myField); + str_.append(myField); + } + return *this; +} + +DataQuery& DataQuery::OrderByDesc(const std::string &field) +{ + std::string myField = field; + if (ValidateField(myField)) { + str_.append(SPACE); + str_.append(ORDER_BY_DESC); + str_.append(SPACE); + EscapeSpace(myField); + str_.append(myField); + } + return *this; +} + +DataQuery& DataQuery::Limit(const int number, const int offset) +{ + if (number < 0 || offset < 0) { + ZLOGE("Invalid number param"); + return *this; + } + str_.append(SPACE); + str_.append(LIMIT); + str_.append(SPACE); + str_.append(BasicToString(number)); + str_.append(SPACE); + str_.append(BasicToString(offset)); + return *this; +} + +DataQuery& DataQuery::BeginGroup() +{ + str_.append(SPACE); + str_.append(BEGIN_GROUP); + return *this; +} + +DataQuery& DataQuery::EndGroup() +{ + str_.append(SPACE); + str_.append(END_GROUP); + return *this; +} + +DataQuery& DataQuery::KeyPrefix(const std::string &prefix) +{ + std::string myPrefix = prefix; + if (ValidateField(myPrefix)) { + str_.append(SPACE); + str_.append(KEY_PREFIX); + str_.append(SPACE); + EscapeSpace(myPrefix); + str_.append(myPrefix); + } + return *this; +} + +DataQuery& DataQuery::SetSuggestIndex(const std::string &index) +{ + std::string suggestIndex = index; + if (ValidateField(suggestIndex)) { + str_.append(SPACE); + str_.append(SUGGEST_INDEX); + str_.append(SPACE); + EscapeSpace(suggestIndex); + str_.append(suggestIndex); + } + return *this; +} + +std::string DataQuery::ToString() const +{ + if (str_.length() > MAX_QUERY_LENGTH) { + ZLOGE("Query is too long"); + return std::string(); + } + std::string str(str_.begin(), str_.end()); + return str; +} + +template +void DataQuery::AppendCommon(const std::string &keyword, const std::string &fieldType, + std::string &field, const T &value) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + str_.append(fieldType); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + str_.append(BasicToString(value)); +} + +void DataQuery::AppendCommonString(const std::string &keyword, const std::string &fieldType, + std::string &field, std::string &value) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + str_.append(fieldType); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + EscapeSpace(value); + str_.append(value); +} + +void DataQuery::AppendCommonBoolean(const std::string &keyword, const std::string &fieldType, + std::string &field, const bool &value) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + str_.append(fieldType); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + if (value) { + str_.append(VALUE_TRUE); + } else { + str_.append(VALUE_FALSE); + } +} + +void DataQuery::AppendCommonString(const std::string &keyword, std::string &field, std::string &value) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + EscapeSpace(value); + str_.append(value); +} + +template +void DataQuery::AppendCommonList(const std::string &keyword, const std::string &fieldType, + std::string &field, const std::vector &valueList) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + str_.append(fieldType); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + str_.append(START_IN); + str_.append(SPACE); + for (T object : valueList) { + str_.append(BasicToString(object)); + str_.append(SPACE); + } + str_.append(END_IN); +} + +void DataQuery::AppendCommonListString(const std::string &keyword, const std::string &fieldType, + std::string &field, std::vector &valueList) +{ + str_.append(SPACE); + str_.append(keyword); + str_.append(SPACE); + str_.append(fieldType); + str_.append(SPACE); + EscapeSpace(field); + str_.append(field); + str_.append(SPACE); + str_.append(START_IN); + str_.append(SPACE); + for (std::string str : valueList) { + EscapeSpace(str); + str_.append(str); + str_.append(SPACE); + } + str_.append(END_IN); +} + +void DataQuery::EscapeSpace(std::string &input) +{ + if (input.length() == 0) { + input = EMPTY_STRING; + } + size_t index = 0; // search from the beginning of the string + while (true) { + index = input.find(DataQuery::SPECIAL, index); + if (index == std::string::npos) { + break; + } + input.replace(index, 1, DataQuery::SPECIAL_ESCAPE); // 1 char to be replaced + index += 3; // replaced with 3 chars, keep searching the remaining string + } + index = 0; // search from the beginning of the string + while (true) { + index = input.find(DataQuery::SPACE, index); + if (index == std::string::npos) { + break; + } + input.replace(index, 1, DataQuery::SPACE_ESCAPE); // 1 char to be replaced + index += 2; // replaced with 2 chars, keep searching the remaining string + } +} + +bool DataQuery::ValidateField(const std::string &field) +{ + if (field.empty() || field.find(DataQuery::SPECIAL) != std::string::npos) { + ZLOGE("invalid string argument"); + return false; + } + return true; +} + +template +std::string DataQuery::BasicToString(const T &value) +{ + std::ostringstream oss; + oss << value; + return oss.str(); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/delegate_mgr_callback.h b/frameworks/innerkitsimpl/distributeddatafwk/src/delegate_mgr_callback.h new file mode 100644 index 000000000..86495fec8 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/delegate_mgr_callback.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 DISTRIBUTEDDATAMGR_DELEGATE_MGR_CALLBACK_H +#define DISTRIBUTEDDATAMGR_DELEGATE_MGR_CALLBACK_H + +#include "db_meta_callback_delegate.h" + +namespace OHOS { +namespace AppDistributedKv { +class DelegateMgrCallback : public DistributedKv::DbMetaCallbackDelegate { +public: + virtual ~DelegateMgrCallback() {} + + explicit DelegateMgrCallback(DistributedDB::KvStoreDelegateManager *delegate) + : delegate_(delegate) {} + + bool GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) override + { + if (IsDestruct()) { + return false; + } + DistributedDB::DBStatus ret = delegate_->GetKvStoreDiskSize(storeId, size); + if (ret != DistributedDB::DBStatus::OK) { + return false; + } + return true; + } + + void GetKvStoreKeys(std::vector &entries) override + { + } + + bool IsDestruct() + { + return delegate_ == nullptr; + } +private: + DistributedDB::KvStoreDelegateManager *delegate_ {}; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_DELEGATE_MGR_CALLBACK_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.cpp new file mode 100755 index 000000000..19e32019b --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.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 "device_status_change_listener_client.h" +#include + +namespace OHOS::DistributedKv { +DeviceStatusChangeListenerClient::DeviceStatusChangeListenerClient( + std::shared_ptr listener) : listener_(std::move(listener)) +{} + +void DeviceStatusChangeListenerClient::OnChange(const DeviceInfo &results, const DeviceChangeType &type) +{ + if (listener_ != nullptr) { + listener_->OnDeviceChanged(results, type); + } +} +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.h new file mode 100644 index 000000000..1f9645079 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEV_DEVICE_STATUS_CHANGE_LISTENER_IMPL_H +#define DEV_DEVICE_STATUS_CHANGE_LISTENER_IMPL_H + +#include "idevice_status_change_listener.h" +#include "device_status_change_listener.h" + +namespace OHOS::DistributedKv { +class DeviceStatusChangeListenerClient : public DeviceStatusChangeListenerStub { +public: + explicit DeviceStatusChangeListenerClient(std::shared_ptr listener); + void OnChange(const DeviceInfo &results, const DeviceChangeType &type) override; + ~DeviceStatusChangeListenerClient() {} +private: + std::shared_ptr listener_; +}; +} +#endif // DEV_DEVICE_STATUS_CHANGE_LISTENER_IMPL_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/distributed_kv_data_manager.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/distributed_kv_data_manager.cpp new file mode 100644 index 000000000..1ac47434e --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/distributed_kv_data_manager.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DistributedKvDataManager" + +#include "distributed_kv_data_manager.h" +#include "constant.h" +#include "ikvstore_data_service.h" +#include "kvstore_client.h" +#include "kvstore_service_death_notifier.h" +#include "log_print.h" +#include "refbase.h" +#include "single_kvstore_client.h" +#include "dds_trace.h" +#include "communication_provider.h" +#include "device_status_change_listener_client.h" + +namespace OHOS { +namespace DistributedKv { +DistributedKvDataManager::DistributedKvDataManager() +{} + +DistributedKvDataManager::~DistributedKvDataManager() +{} + +void DistributedKvDataManager::GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (storeIdTmp.size() == 0 || storeIdTmp.size() > Constant::MAX_STORE_ID_LENGTH) { + callback(Status::INVALID_ARGUMENT, nullptr); + ZLOGE("invalid storeId."); + return; + } + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + Status status = Status::SERVER_UNAVAILABLE; + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + callback(status, nullptr); + return; + } + + ZLOGD("call proxy."); + sptr proxyTmp; + status = kvDataServiceProxy->GetKvStore(options, appId, storeId, + [&](sptr proxy) { proxyTmp = std::move(proxy); }); + if (status == Status::RECOVER_SUCCESS) { + ZLOGE("proxy recover success: %d", static_cast(status)); + callback(status, std::make_unique(std::move(proxyTmp), storeIdTmp)); + return; + } + + if (status != Status::SUCCESS) { + ZLOGE("proxy return error: %d", static_cast(status)); + callback(status, nullptr); + return; + } + + if (proxyTmp == nullptr) { + ZLOGE("proxy return nullptr."); + callback(status, nullptr); + return; + } + + callback(status, std::make_unique(std::move(proxyTmp), storeIdTmp)); +} + +void DistributedKvDataManager::GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (storeIdTmp.size() == 0 || storeIdTmp.size() > Constant::MAX_STORE_ID_LENGTH) { + callback(Status::INVALID_ARGUMENT, nullptr); + ZLOGE("invalid storeId."); + return; + } + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + Status status = Status::SERVER_UNAVAILABLE; + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + callback(status, nullptr); + return; + } + + ZLOGD("call proxy."); + sptr proxyTmp; + status = kvDataServiceProxy->GetSingleKvStore(options, appId, storeId, + [&](sptr proxy) { proxyTmp = std::move(proxy); }); + if (status == Status::RECOVER_SUCCESS) { + ZLOGE("proxy recover success: %d", static_cast(status)); + callback(status, std::make_unique(std::move(proxyTmp), storeIdTmp)); + return; + } + + if (status != Status::SUCCESS) { + ZLOGE("proxy return error: %d", static_cast(status)); + callback(status, nullptr); + return; + } + + if (proxyTmp == nullptr) { + ZLOGE("proxy return nullptr."); + callback(status, nullptr); + return; + } + + callback(status, std::make_unique(std::move(proxyTmp), storeIdTmp)); +} + +void DistributedKvDataManager::GetAllKvStoreId(const AppId &appId, + std::function &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + std::vector storeIds; + callback(Status::SERVER_UNAVAILABLE, storeIds); + return; + } + + kvDataServiceProxy->GetAllKvStoreId(appId, callback); +} + +Status DistributedKvDataManager::CloseKvStore(const AppId &appId, const StoreId &storeId, + std::unique_ptr kvStorePtr) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KvStoreServiceDeathNotifier::SetAppId(appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (storeIdTmp.size() == 0 || storeIdTmp.size() > Constant::MAX_STORE_ID_LENGTH) { + ZLOGE("invalid storeId."); + return Status::INVALID_ARGUMENT; + } + + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy != nullptr) { + return kvDataServiceProxy->CloseKvStore(appId, storeId); + } + ZLOGE("proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status DistributedKvDataManager::CloseKvStore(const AppId &appId, std::unique_ptr kvStorePtr) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (kvStorePtr == nullptr) { + ZLOGE("kvStorePtr is nullptr."); + return Status::INVALID_ARGUMENT; + } + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy != nullptr) { + return kvDataServiceProxy->CloseKvStore(appId, kvStorePtr->GetStoreId()); + } + ZLOGE("proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status DistributedKvDataManager::CloseAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy != nullptr) { + return kvDataServiceProxy->CloseAllKvStore(appId); + } + ZLOGE("proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status DistributedKvDataManager::DeleteKvStore(const AppId &appId, const StoreId &storeId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (storeIdTmp.size() == 0 || storeIdTmp.size() > Constant::MAX_STORE_ID_LENGTH) { + ZLOGE("invalid storeId."); + return Status::INVALID_ARGUMENT; + } + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy != nullptr) { + return kvDataServiceProxy->DeleteKvStore(appId, storeId); + } + ZLOGE("proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status DistributedKvDataManager::DeleteAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KvStoreServiceDeathNotifier::SetAppId(appId); + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy != nullptr) { + return kvDataServiceProxy->DeleteAllKvStore(appId); + } + ZLOGE("proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +void DistributedKvDataManager::RegisterKvStoreServiceDeathRecipient( + std::shared_ptr kvStoreDeathRecipient) +{ + ZLOGD("begin"); + if (kvStoreDeathRecipient == nullptr) { + ZLOGW("Register KvStoreService Death Recipient input is null."); + return; + } + std::shared_ptr kvStoreDeathRecipientImpl = + std::make_shared(kvStoreDeathRecipient); + if (kvStoreDeathRecipientImpl != nullptr) { + KvStoreServiceDeathNotifier::AddServiceDeathWatcher(kvStoreDeathRecipientImpl); + } else { + ZLOGW("Register KvStoreService Death Recipient failed."); + } +} + +void DistributedKvDataManager::UnRegisterKvStoreServiceDeathRecipient( + std::shared_ptr kvStoreDeathRecipient) +{ + ZLOGD("begin"); + if (kvStoreDeathRecipient == nullptr) { + ZLOGW("UnRegister KvStoreService Death Recipient input is null."); + return; + } + std::shared_ptr kvStoreDeathRecipientImpl = + std::make_shared(kvStoreDeathRecipient); + if (kvStoreDeathRecipientImpl != nullptr) { + KvStoreServiceDeathNotifier::RemoveServiceDeathWatcher(kvStoreDeathRecipientImpl); + } else { + ZLOGW("UnRegister KvStoreService Death Recipient failed."); + } +} + +Status DistributedKvDataManager::GetLocalDevice(DeviceInfo &localDevice) +{ + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + return Status::ERROR; + } + + return kvDataServiceProxy->GetLocalDevice(localDevice); +} + +Status DistributedKvDataManager::GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) +{ + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + return Status::ERROR; + } + + return kvDataServiceProxy->GetDeviceList(deviceInfoList, strategy); +} + +static std::map> deviceObservers_; +static std::mutex deviceObserversMapMutex_; +Status DistributedKvDataManager::StartWatchDeviceChange(std::shared_ptr observer) +{ + sptr ipcObserver = new(std::nothrow) DeviceStatusChangeListenerClient(observer); + if (ipcObserver == nullptr) { + ZLOGW("new DeviceStatusChangeListenerClient failed"); + return Status::ERROR; + } + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + return Status::ERROR; + } + Status status = kvDataServiceProxy->StartWatchDeviceChange(ipcObserver, observer->GetFilterStrategy()); + if (status == Status::SUCCESS) { + { + std::lock_guard lck(deviceObserversMapMutex_); + deviceObservers_.insert({observer.get(), ipcObserver}); + } + return Status::SUCCESS; + } + ZLOGE("watch failed."); + return Status::ERROR; +} + +Status DistributedKvDataManager::StopWatchDeviceChange(std::shared_ptr observer) +{ + sptr kvDataServiceProxy = KvStoreServiceDeathNotifier::GetDistributedKvDataService(); + if (kvDataServiceProxy == nullptr) { + ZLOGE("proxy is nullptr."); + return Status::ERROR; + } + std::lock_guard lck(deviceObserversMapMutex_); + auto it = deviceObservers_.find(observer.get()); + if (it == deviceObservers_.end()) { + ZLOGW(" not start watch device change."); + return Status::ERROR; + } + Status status = kvDataServiceProxy->StopWatchDeviceChange(it->second); + if (status == Status::SUCCESS) { + deviceObservers_.erase(it->first); + } else { + ZLOGW("stop watch failed code=%d.", static_cast(status)); + } + return status; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/idevice_status_change_listener_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/idevice_status_change_listener_impl.cpp new file mode 100755 index 000000000..3900444fb --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/idevice_status_change_listener_impl.cpp @@ -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. + */ + +#define LOG_TAG "DevChangeStatusListener" + +#include "idevice_status_change_listener.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +enum { + ONCHANGE, +}; +DeviceStatusChangeListenerProxy::DeviceStatusChangeListenerProxy(const sptr &impl) + : IRemoteProxy(impl) +{} + +void DeviceStatusChangeListenerProxy::OnChange(const DeviceInfo &results, const DeviceChangeType &type) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(DeviceStatusChangeListenerProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteInt32(static_cast(type)) || !results.Marshalling(data)) { + ZLOGW("SendRequest write parcel type failed."); + return; + } + MessageOption mo { MessageOption::TF_ASYNC }; + int error = Remote()->SendRequest(ONCHANGE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error %d", error); + } +} + +int DeviceStatusChangeListenerStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = DeviceStatusChangeListenerStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + switch (code) { + case ONCHANGE: { + DeviceChangeType type = static_cast(data.ReadInt32()); + DeviceInfo *deviceInfoPtr = DeviceInfo::UnMarshalling(data); + if (deviceInfoPtr != nullptr) { + OnChange(*deviceInfoPtr, type); + delete deviceInfoPtr; + } else { + ZLOGW("device info is null"); + } + return 0; + } + default: + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore.cpp new file mode 100755 index 000000000..823907b15 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore.cpp @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreImplProxy" + +#include "ikvstore.h" +#include +#include "message_parcel.h" +#include "constant.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +enum { + GETKVSTORESNAPSHOT, + RELEASEKVSTORESNAPSHOT, + PUT, + PUTBATCH, + DELETE, + DELETEBATCH, + CLEAR, + STARTTRANSACTION, + COMMIT, + ROLLBACK, + SUBSCRIBEKVSTORE, + UNSUBSCRIBEKVSTORE, +}; + +KvStoreImplProxy::KvStoreImplProxy(const sptr &impl) : IRemoteProxy(impl) +{} + +void KvStoreImplProxy::GetKvStoreSnapshot(sptr observer, + std::function)> callback) +{ + if (observer == nullptr) { + callback(Status::INVALID_ARGUMENT, nullptr); + return; + } + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + ZLOGW("get snapshot fail."); + callback(Status::IPC_ERROR, nullptr); + return; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETKVSTORESNAPSHOT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + callback(Status::IPC_ERROR, nullptr); + return; + } + Status status = static_cast(reply.ReadInt32()); + if (status == Status::SUCCESS) { + sptr remote = reply.ReadRemoteObject(); + if (remote == nullptr) { + callback(status, nullptr); + return; + } + sptr kvstoreImplProxy = iface_cast(remote); + callback(status, std::move(kvstoreImplProxy)); + } else { + callback(status, nullptr); + } +} + +Status KvStoreImplProxy::ReleaseKvStoreSnapshot(sptr kvStoreSnapshotPtr) +{ + if (kvStoreSnapshotPtr == nullptr) { + ZLOGW("input snapshot ptr is null"); + return Status::INVALID_ARGUMENT; + } + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteRemoteObject(kvStoreSnapshotPtr->AsObject().GetRefPtr())) { + ZLOGW("write input snapshot ptr failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(RELEASEKVSTORESNAPSHOT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::Put(const Key &key, const Value &value) +{ + ZLOGD("proxy put"); + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("write capacity failed."); + return Status::IPC_ERROR; + } + + int bufferSize = key.RawSize() + value.RawSize(); + if (!data.WriteInt32(bufferSize)) { + ZLOGW("write size failed."); + return Status::IPC_ERROR; + } + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + if (!data.WriteParcelable(&key) || !data.WriteParcelable(&value)) { + ZLOGW("write key or value failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int error = Remote()->SendRequest(PUT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed with error code %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); + } + ZLOGI("putting large data."); + std::unique_ptr buffer(new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::ERROR; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + if (!key.WriteToBuffer(cursor, bufferLeftSize) || + !value.WriteToBuffer(cursor, bufferLeftSize) || + !data.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write failed"); + return Status::ERROR; + } + // Parcel before IPC: + // buffer: options | bufferSize + // rawdata: keySize | key | ValueSize | value + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(PUT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::PutBatch(const std::vector &entries) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set capacity failed."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(entries.size())) { + ZLOGW("write entries size failed."); + return Status::IPC_ERROR; + } + + int64_t bufferSize = 0; + for (const auto &item : entries) { + if (item.key.Size() > Constant::MAX_KEY_LENGTH || item.value.Size() > Constant::MAX_VALUE_LENGTH) { + return Status::INVALID_ARGUMENT; + } + bufferSize += item.key.RawSize() + item.value.RawSize(); + } + if (!data.WriteInt32(bufferSize)) { + ZLOGW("write buffer size failed."); + return Status::IPC_ERROR; + } + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (const auto &item : entries) { + if (!data.WriteParcelable(&item)) { + ZLOGW("write parcel failed."); + return Status::IPC_ERROR; + } + } + MessageOption mo { MessageOption::TF_SYNC }; + if (Remote()->SendRequest(PUTBATCH, data, reply, mo) != 0) { + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); + } + ZLOGI("putting large data."); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("batch size larger than Messageparcel limit.(%" PRIu64")", bufferSize); + return Status::INVALID_ARGUMENT; + } + std::unique_ptr buffer(new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::ERROR; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : entries) { + if (!item.key.WriteToBuffer(cursor, bufferLeftSize) || + !item.value.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write item failed."); + } + } + if (!data.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write failed"); + return Status::ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(PUTBATCH, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::Delete(const Key &key) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteParcelable(&key)) { + ZLOGW("write key failed."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::DeleteBatch(const std::vector &keys) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set capacity failed."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(keys.size())) { + ZLOGW("write keys size failed."); + return Status::IPC_ERROR; + } + for (const auto &item : keys) { + if (item.Size() > Constant::MAX_KEY_LENGTH) { + ZLOGW("Delete key size larger than key size limit"); + return Status::INVALID_ARGUMENT; + } + if (!data.WriteParcelable(&item)) { + ZLOGW("write parcel failed"); + return Status::IPC_ERROR; + } + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETEBATCH, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::Clear() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(CLEAR, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::StartTransaction() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(STARTTRANSACTION, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::Commit() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(COMMIT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::Rollback() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(ROLLBACK, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::SubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + if (observer == nullptr) { + ZLOGW("observer is invalid."); + return Status::INVALID_ARGUMENT; + } + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(subscribeType)) || + !data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + ZLOGW("subscribe type failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(SUBSCRIBEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreImplProxy::UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(subscribeType)) || + !data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + ZLOGW("unsubscribe type failed."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(UNSUBSCRIBEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +int32_t KvStoreImplStub::GetKvStoreSnapshotOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write obj failed."); + return -1; + } + return 0; + } + sptr kvStoreObserverProxy = iface_cast(remote); + sptr proxyTmp; + Status statusTmp; + GetKvStoreSnapshot(kvStoreObserverProxy, [&](Status status, sptr proxy) { + statusTmp = status; + proxyTmp = std::move(proxy); + }); + if (!reply.WriteInt32(static_cast(statusTmp))) { + ZLOGW("write get snapshot result failed."); + return -1; + } + if (statusTmp == Status::SUCCESS && proxyTmp != nullptr) { + if (!reply.WriteRemoteObject(proxyTmp->AsObject().GetRefPtr())) { + ZLOGW("write strong failed."); + return -1; + } + } + return 0; +} +int32_t KvStoreImplStub::ReleaseKvStoreSnapshotOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("kvstoreSnapshotProxy nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr kvStoreSnapshotProxy = iface_cast(remote); + Status status = ReleaseKvStoreSnapshot(kvStoreSnapshotProxy); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write release snapshot failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::PutOnRemoteRequest(MessageParcel &data, MessageParcel &reply) +{ + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("write capacity failed"); + return -1; + } + const int bufferSize = data.ReadInt32(); + ZLOGD("bufferSize %d", bufferSize); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + sptr key = data.ReadParcelable(); + sptr value = data.ReadParcelable(); + if (key == nullptr || value == nullptr) { + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write key or val status failed."); + return -1; + } + return 0; + } + Status status = Put(*key, *value); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write ret status failed."); + return -1; + } + return 0; + } + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(data.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write buffer status failed."); + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + const uint8_t *cursor = buffer; + Key key; + Value value; + if (!key.ReadFromBuffer(cursor, bufferLeftSize) || !value.ReadFromBuffer(cursor, bufferLeftSize)) { + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("read key or value error."); + return -1; + } + } + Status status = Put(key, value); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write ret status failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::PutBatchOnRemoteRequest(MessageParcel &data, MessageParcel &reply) +{ + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set batch size failed."); + return -1; + } + int len = data.ReadInt32(); + if (len < 0) { + ZLOGW("invalid status. len %d", len); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write putbatch failed."); + return -1; + } + return 0; + } + const int bufferSize = data.ReadInt32(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + std::vector entries; + for (int i = 0; i < len; i++) { + sptr entry = data.ReadParcelable(); + if (entry == nullptr) { + ZLOGW("putbatch got null entry pointer"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write putbatch failed."); + return -1; + } + return 0; + } + entries.push_back(*entry); + } + Status status = PutBatch(entries); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write putbatch failed."); + return -1; + } + return 0; + } + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(data.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + const uint8_t *cursor = buffer; + std::vector entries; + Entry entry; + for (int i = 0; i < len; i++) { + bool success = entry.key.ReadFromBuffer(cursor, bufferLeftSize); + success = success && entry.value.ReadFromBuffer(cursor, bufferLeftSize); + entries.push_back(std::move(entry)); + if (!success) { + ZLOGW("get key or value failed"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; + } + } + Status status = PutBatch(entries); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::DeleteOnRemoteRequest(MessageParcel &data, MessageParcel &reply) +{ + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; + } + Status status = Delete(*key); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::DeleteBatchOnRemoteRequest(MessageParcel &data, MessageParcel &reply) +{ + int len = data.ReadInt32(); + if (len < 0) { + ZLOGW("len %d invalid after ipc", len); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; + } + std::vector keys; + for (int i = 0; i < len; i++) { + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key nullptr"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; + } + keys.push_back(*key); + } + Status status = DeleteBatch(keys); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::SubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + int32_t type = data.ReadInt32(); + if (type < 0) { + return -1; + } + SubscribeType subscribeType = static_cast(type); + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("kvStoreObserverProxy is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr kvStoreObserverProxy = iface_cast(remote); + Status status = SubscribeKvStore(subscribeType, std::move(kvStoreObserverProxy)); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write subscribe status failed."); + return -1; + } + return 0; +} +int32_t KvStoreImplStub::UnSubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + int32_t type = data.ReadInt32(); + if (type < 0) { + return -1; + } + SubscribeType subscribeType = static_cast(type); + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("unsubscribe Proxy is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr kvStoreObserverProxy = iface_cast(remote); + Status status = UnSubscribeKvStore(subscribeType, std::move(kvStoreObserverProxy)); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write unsubscribe status failed."); + return -1; + } + return 0; +} + +int32_t KvStoreImplStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = KvStoreImplStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + switch (code) { + case GETKVSTORESNAPSHOT: { + return GetKvStoreSnapshotOnRemote(data, reply); + } + case RELEASEKVSTORESNAPSHOT: { + return ReleaseKvStoreSnapshotOnRemote(data, reply); + } + case PUT: { + return PutOnRemoteRequest(data, reply); + } + case PUTBATCH: { + return PutBatchOnRemoteRequest(data, reply); + } + case DELETE: { + return DeleteOnRemoteRequest(data, reply); + } + case DELETEBATCH: { + return DeleteBatchOnRemoteRequest(data, reply); + } + case CLEAR: { + Status status = Clear(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write clear failed."); + return -1; + } + return 0; + } + case STARTTRANSACTION: { + Status status = StartTransaction(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write transaction failed."); + return -1; + } + return 0; + } + case COMMIT: { + Status status = Commit(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write commit failed."); + return -1; + } + return 0; + } + case ROLLBACK: { + Status status = Rollback(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write rollback failed."); + return -1; + } + return 0; + } + case SUBSCRIBEKVSTORE: { + return SubscribeKvStoreOnRemote(data, reply); + } + case UNSUBSCRIBEKVSTORE: { + return UnSubscribeKvStoreOnRemote(data, reply); + } + default: { + MessageOption mo { MessageOption::TF_SYNC }; + return IPCObjectStub::OnRemoteRequest(code, data, reply, mo); + } + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_client_death_observer.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_client_death_observer.cpp new file mode 100644 index 000000000..4299647b2 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_client_death_observer.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 "ikvstore_client_death_observer.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreClientDeathObserverProxy::KvStoreClientDeathObserverProxy(const sptr &impl) + : IRemoteProxy(impl) +{} + +int32_t KvStoreClientDeathObserverStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_data_service.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_data_service.cpp new file mode 100755 index 000000000..7cd68ee1c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_data_service.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreDataServiceProxy" + +#include "ikvstore_data_service.h" +#include "constant.h" +#include "message_parcel.h" +#include "types.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +constexpr KvStoreDataServiceStub::RequestHandler KvStoreDataServiceStub::HANDLERS[SERVICE_CMD_LAST]; +KvStoreDataServiceProxy::KvStoreDataServiceProxy(const sptr &impl) + : IRemoteProxy(impl) +{ + ZLOGI("init data service proxy."); +} + +Status KvStoreDataServiceProxy::GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + ZLOGI("%s %s", appId.appId.c_str(), storeId.storeId.c_str()); + MessageParcel data; + MessageParcel reply; + + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + + // Passing a struct with an std::string field is a potential security exploit. + OptionsIpc optionsIpc; + optionsIpc.createIfMissing = options.createIfMissing; + optionsIpc.encrypt = options.encrypt; + optionsIpc.persistant = options.persistant; + optionsIpc.backup = options.backup; + optionsIpc.autoSync = options.autoSync; + optionsIpc.securityLevel = options.securityLevel; + optionsIpc.syncPolicy = options.syncPolicy; + optionsIpc.kvStoreType = options.kvStoreType; + optionsIpc.syncable = options.syncable; + optionsIpc.dataOwnership = true; // set default value + + if (!data.WriteBuffer(&optionsIpc, sizeof(optionsIpc)) || + !data.WriteString(appId.appId) || + !data.WriteString(storeId.storeId)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + Status ret = static_cast(reply.ReadInt32()); + if (ret == Status::SUCCESS) { + sptr remote = reply.ReadRemoteObject(); + if (remote != nullptr) { + sptr kvstoreImplProxy = iface_cast(remote); + callback(std::move(kvstoreImplProxy)); + } + } else { + callback(nullptr); + } + return ret; +} + +Status KvStoreDataServiceProxy::GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + ZLOGI("%s %s", appId.appId.c_str(), storeId.storeId.c_str()); + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("SetMaxCapacity failed."); + return Status::IPC_ERROR; + } + // Passing a struct with an std::string field is a potential security exploit. + OptionsIpc optionsIpc; + optionsIpc.createIfMissing = options.createIfMissing; + optionsIpc.encrypt = options.encrypt; + optionsIpc.persistant = options.persistant; + optionsIpc.backup = options.backup; + optionsIpc.autoSync = options.autoSync; + optionsIpc.securityLevel = options.securityLevel; + optionsIpc.syncPolicy = options.syncPolicy; + optionsIpc.kvStoreType = options.kvStoreType; + optionsIpc.syncable = options.syncable; + optionsIpc.dataOwnership = true; // set default value + std::string schemaString = options.schema; + + if (!data.WriteBuffer(&optionsIpc, sizeof(OptionsIpc)) || + !data.WriteString(appId.appId) || + !data.WriteString(storeId.storeId) || + !data.WriteString(schemaString)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETSINGLEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status == Status::SUCCESS) { + sptr remote = reply.ReadRemoteObject(); + if (remote != nullptr) { + sptr kvstoreImplProxy = iface_cast(remote); + callback(std::move(kvstoreImplProxy)); + } + } else { + callback(nullptr); + } + return status; +} + +void KvStoreDataServiceProxy::GetAllKvStoreId(const AppId &appId, + std::function &)> callback) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteString(appId.appId)) { + ZLOGW("failed to write parcel."); + return; + } + std::vector storeIds; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETALLKVSTOREID, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + callback(Status::IPC_ERROR, storeIds); + return; + } + std::vector stores; + reply.ReadStringVector(&stores); + for (const auto &id: stores) { + storeIds.push_back({id}); + } + Status status = static_cast(reply.ReadInt32()); + callback(status, storeIds); +} + +Status KvStoreDataServiceProxy::CloseKvStore(const AppId &appId, const StoreId &storeId) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(appId.appId) || + !data.WriteString(storeId.storeId)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(CLOSEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +/* close all opened kvstore */ +Status KvStoreDataServiceProxy::CloseAllKvStore(const AppId &appId) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(appId.appId)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(CLOSEALLKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreDataServiceProxy::DeleteKvStore(const AppId &appId, const StoreId &storeId) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(appId.appId) || + !data.WriteString(storeId.storeId)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +/* delete all kv store */ +Status KvStoreDataServiceProxy::DeleteAllKvStore(const AppId &appId) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(appId.appId)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETEALLKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreDataServiceProxy::RegisterClientDeathObserver(const AppId &appId, sptr observer) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(appId.appId)) { + ZLOGW("failed to write string."); + return Status::IPC_ERROR; + } + if (observer != nullptr) { + if (!data.WriteRemoteObject(observer)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + } else { + return Status::INVALID_ARGUMENT; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(REGISTERCLIENTDEATHOBSERVER, data, reply, mo); + if (error != 0) { + ZLOGW("failed during IPC. errCode %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreDataServiceProxy::GetLocalDevice(OHOS::DistributedKv::DeviceInfo &device) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETLOCALDEVICE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status == Status::SUCCESS) { + device = {reply.ReadString(), reply.ReadString(), reply.ReadString()}; + } + return status; +} + +Status KvStoreDataServiceProxy::GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(strategy))) { + ZLOGW("write int failed."); + return Status::IPC_ERROR; + } + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETDEVICELIST, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status == Status::SUCCESS) { + int len = reply.ReadInt32(); + for (int i = 0; i < len; i++) { + DeviceInfo deviceInfo = { + .deviceId = reply.ReadString(), + .deviceName = reply.ReadString(), + .deviceType = reply.ReadString() + }; + deviceInfoList.push_back(std::move(deviceInfo)); + } + } + return status; +} + +Status KvStoreDataServiceProxy::StartWatchDeviceChange(sptr observer, + DeviceFilterStrategy strategy) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(strategy))) { + ZLOGW("write int failed."); + return Status::IPC_ERROR; + } + if (observer != nullptr) { + if (!data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + return Status::IPC_ERROR; + } + } else { + return Status::INVALID_ARGUMENT; + } + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(STARTWATCHDEVICECHANGE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status KvStoreDataServiceProxy::StopWatchDeviceChange(sptr observer) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(KvStoreDataServiceProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (observer != nullptr) { + if (!data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + return Status::IPC_ERROR; + } + } else { + return Status::INVALID_ARGUMENT; + } + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(STOPWATCHDEVICECHANGE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +int32_t KvStoreDataServiceStub::GetKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + OptionsIpc optionsIpc; + AppId appId; + StoreId storeId; + + const OptionsIpc *optionIpcPtr = reinterpret_cast(data.ReadBuffer(sizeof(OptionsIpc))); + if (optionIpcPtr == nullptr) { + ZLOGW("optionPtr is nullptr"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + optionsIpc = *optionIpcPtr; + Options options; + options.createIfMissing = optionsIpc.createIfMissing; + options.encrypt = optionsIpc.encrypt; + options.persistant = optionsIpc.persistant; + options.backup = optionsIpc.backup; + options.autoSync = optionsIpc.autoSync; + options.securityLevel = optionsIpc.securityLevel; + options.syncPolicy = optionsIpc.syncPolicy; + options.kvStoreType = optionsIpc.kvStoreType; + options.syncable = optionsIpc.syncable; + options.dataOwnership = optionsIpc.dataOwnership; + appId.appId = data.ReadString(); + storeId.storeId = data.ReadString(); + sptr proxyTmp; + Status status = GetKvStore(options, appId, storeId, + [&proxyTmp](sptr proxy) { proxyTmp = std::move(proxy); }); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + if (proxyTmp == nullptr) { + ZLOGW("proxy is null."); + return 0; + } + if (status == Status::SUCCESS && !reply.WriteRemoteObject(proxyTmp->AsObject().GetRefPtr())) { + ZLOGW("write ipc failed."); + return -1; + } + return 0; +} +int32_t KvStoreDataServiceStub::GetAllKvStoreIdOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + appId.appId = data.ReadString(); + std::vector storeIdList; + Status statusTmp; + GetAllKvStoreId(appId, [&](Status status, std::vector &storeIds) { + for (const auto &id : storeIds) { + storeIdList.push_back(id.storeId); + } + statusTmp = status; + }); + + if (!reply.WriteStringVector(storeIdList)) { + return -1; + } + + if (!reply.WriteInt32(static_cast(statusTmp))) { + return -1; + } + return 0; +} +int32_t KvStoreDataServiceStub::GetDeviceListOnRemote(MessageParcel &data, MessageParcel &reply) +{ + std::vector infos; + DeviceFilterStrategy strategy = static_cast(data.ReadInt32()); + Status status = GetDeviceList(infos, strategy); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + if (status == Status::SUCCESS) { + if (!reply.WriteInt32(infos.size())) { + return -1; + } + for (DeviceInfo const &info : infos) { + if (!reply.WriteString(info.deviceId) || !reply.WriteString(info.deviceName) || + !reply.WriteString(info.deviceType)) { + return -1; + } + } + } + return 0; +} +int32_t KvStoreDataServiceStub::StartWatchDeviceChangeOnRemote(MessageParcel &data, MessageParcel &reply) +{ + DeviceFilterStrategy strategy = static_cast(data.ReadInt32()); + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("observerProxy nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr observerProxy = iface_cast(remote); + Status status = StartWatchDeviceChange(std::move(observerProxy), strategy); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int32_t KvStoreDataServiceStub::StopWatchDeviceChangeOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("observerProxy nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr observerProxy = iface_cast(remote); + Status status = StopWatchDeviceChange(std::move(observerProxy)); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int32_t KvStoreDataServiceStub::GetSingleKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + OptionsIpc optionsIpc; + AppId appId; + StoreId storeId; + const OptionsIpc *optionIpcPtr = reinterpret_cast(data.ReadBuffer(sizeof(OptionsIpc))); + if (optionIpcPtr == nullptr) { + ZLOGW("optionPtr is nullptr"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + optionsIpc = *optionIpcPtr; + appId.appId = data.ReadString(); + storeId.storeId = data.ReadString(); + Options options; + options.createIfMissing = optionsIpc.createIfMissing; + options.encrypt = optionsIpc.encrypt; + options.persistant = optionsIpc.persistant; + options.backup = optionsIpc.backup; + options.autoSync = optionsIpc.autoSync; + options.securityLevel = optionsIpc.securityLevel; + options.syncPolicy = optionsIpc.syncPolicy; + options.kvStoreType = optionsIpc.kvStoreType; + options.syncable = optionsIpc.syncable; + options.dataOwnership = optionsIpc.dataOwnership; + options.schema = data.ReadString(); + sptr proxyTmp; + Status status = GetSingleKvStore(options, appId, storeId, + [&](sptr proxy) { proxyTmp = std::move(proxy); }); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + if (status == Status::SUCCESS && proxyTmp != nullptr) { + if (!reply.WriteRemoteObject(proxyTmp->AsObject().GetRefPtr())) { + return -1; + } + } + return 0; +} + +int32_t KvStoreDataServiceStub::CloseKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + StoreId storeId; + appId.appId = data.ReadString(); + storeId.storeId = data.ReadString(); + Status status = CloseKvStore(appId, storeId); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::CloseAllKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + appId.appId = data.ReadString(); + Status status = CloseAllKvStore(appId); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::DeleteKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + StoreId storeId; + appId.appId = data.ReadString(); + storeId.storeId = data.ReadString(); + Status status = DeleteKvStore(appId, storeId); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::DeleteAllKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + appId.appId = data.ReadString(); + Status status = DeleteAllKvStore(appId); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::RegisterClientDeathObserverOnRemote(MessageParcel &data, MessageParcel &reply) +{ + AppId appId; + appId.appId = data.ReadString(); + sptr kvStoreClientDeathObserverProxy = data.ReadRemoteObject(); + if (kvStoreClientDeathObserverProxy == nullptr) { + return -1; + } + Status status = RegisterClientDeathObserver(appId, std::move(kvStoreClientDeathObserverProxy)); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::GetLocalDeviceOnRemote(MessageParcel &data, MessageParcel &reply) +{ + DeviceInfo info; + Status status = GetLocalDevice(info); + if (!reply.WriteInt32(static_cast(status)) || !reply.WriteString(info.deviceId) || + !reply.WriteString(info.deviceName) || !reply.WriteString(info.deviceType)) { + return -1; + } + return 0; +} + +int32_t KvStoreDataServiceStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = KvStoreDataServiceStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + if (code >= 0 && code < SERVICE_CMD_LAST) { + return (this->*HANDLERS[code])(data, reply); + } else { + MessageOption mo { MessageOption::TF_SYNC }; + return IPCObjectStub::OnRemoteRequest(code, data, reply, mo); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_observer.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_observer.cpp new file mode 100644 index 000000000..53ed766b9 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_observer.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreObserverProxy" + +#include "ikvstore_observer.h" +#include +#include "constant.h" +#include "log_print.h" +#include "message_parcel.h" + +namespace OHOS { +namespace DistributedKv { +using namespace std::chrono; + +enum { + ONCHANGE, +}; + +KvStoreObserverProxy::KvStoreObserverProxy(const sptr &impl) : IRemoteProxy(impl) +{} + +int64_t GetBufferSize(const std::list &entries) +{ + int64_t bufferSize = 0; + for (const auto &item : entries) { + bufferSize += item.key.RawSize() + item.value.RawSize(); + } + return bufferSize; +} + +bool WriteEntryToParcelByBuf(MessageParcel &data, const int64_t &bufferSize, const std::list &list) +{ + std::unique_ptr buffer(new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGE("buffer is null"); + return false; + } + int bufLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : list) { + if (!item.key.WriteToBuffer(cursor, bufLeftSize) || + !item.value.WriteToBuffer(cursor, bufLeftSize)) { + ZLOGE("write item to buff failed"); + return false; + } + } + if (!data.WriteRawData(buffer.get(), bufferSize)) { + ZLOGE("bigDataOnchange write RawData from buff failed"); + return false; + } + return true; +} + +bool WriteListToParcelByBuf(MessageParcel &data, const int64_t &bufferSize, const std::list &list) +{ + if (!data.WriteInt32(list.size()) || + !data.WriteInt32(bufferSize)) { + ZLOGE("write entriesLen or bufferSize fails"); + return false; + } + if (bufferSize == 0) { + return true; + } + + if (!WriteEntryToParcelByBuf(data, bufferSize, list)) { + ZLOGE("bigDataOnchange write RawData to parcel failed"); + return false; + } + return true; +} + +void KvStoreObserverProxy::OnChange(const ChangeNotification &changeNotification, sptr snapshot) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreObserverProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + int64_t insertBufferSize = GetBufferSize(changeNotification.GetInsertEntries()); + int64_t updateBufferSize = GetBufferSize(changeNotification.GetUpdateEntries()); + int64_t deleteBufferSize = GetBufferSize(changeNotification.GetDeleteEntries()); + int64_t totalBufferSize = insertBufferSize + updateBufferSize + deleteBufferSize + sizeof(bool); + if (!data.WriteInt32(totalBufferSize)) { + ZLOGE("Write ChangeNotification buffer size to parcel failed."); + return; + } + ZLOGD("I(%lld) U(%lld) D(%lld) T(%lld)", static_cast(insertBufferSize), + static_cast(updateBufferSize), static_cast(deleteBufferSize), + static_cast(totalBufferSize)); + if (totalBufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + if (!data.WriteParcelable(&changeNotification)) { + ZLOGW("Write ChangeNotification to parcel failed."); + return; + } + } else { + if (!WriteListToParcelByBuf(data, insertBufferSize, changeNotification.GetInsertEntries()) || + !WriteListToParcelByBuf(data, updateBufferSize, changeNotification.GetUpdateEntries()) || + !WriteListToParcelByBuf(data, deleteBufferSize, changeNotification.GetDeleteEntries()) || + !data.WriteString(changeNotification.GetDeviceId()) || + !data.WriteBool(changeNotification.IsClear())) { + ZLOGE("WriteChangeList to Parcel by buffer failed"); + return; + } + } + + if (snapshot != nullptr && !data.WriteRemoteObject(snapshot->AsObject().GetRefPtr())) { + ZLOGE("write strong parcel failed."); + return; + } + + MessageOption mo { MessageOption::TF_WAIT_TIME }; + int error = Remote()->SendRequest(ONCHANGE, data, reply, mo); + if (error != 0) { + ZLOGE("SendRequest failed, error %d", error); + } +} + +bool ReadFromBuff(MessageParcel &data, const int &len, const int &bufferSize, std::list &entries) +{ + const uint8_t *buffer = reinterpret_cast(data.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGE("new buffer filed"); + return false; + } + int bufferLeftSize = bufferSize; + const uint8_t *cursor = buffer; + Entry entry; + for (int i = 0; i < len; i++) { + if (!entry.key.ReadFromBuffer(cursor, bufferLeftSize) || + !entry.value.ReadFromBuffer(cursor, bufferLeftSize)) { + ZLOGE("read key and value from buff failed"); + return false; + } + entries.push_back(std::move(entry)); + } + return true; +} + +bool ReadListFromBuf(MessageParcel &data, std::list &entries) +{ + int len = data.ReadInt32(); + if (len < 0) { + ZLOGE("read onChangeLen failed len %d", len); + return false; + } + int bufferSize = data.ReadInt32(); + if (bufferSize == 0) { + return true; + } + if (!ReadFromBuff(data, len, bufferSize, entries)) { + ZLOGE("bigDataOnchange read buff from parcel filed"); + return false; + } + return true; +} + +int32_t KvStoreObserverStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = KvStoreObserverStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + switch (code) { + case ONCHANGE: { + const int errorResult = -1; + int totalBuffSize = data.ReadInt32(); + if (totalBuffSize < Constant::SWITCH_RAW_DATA_SIZE) { + sptr changeNotification = data.ReadParcelable(); + if (changeNotification == nullptr) { + ZLOGE("changeNotification is nullptr"); + return errorResult; + } + sptr remote = data.ReadRemoteObject(); + if (remote != nullptr) { + sptr kvStoreSnapshotProxy = iface_cast(remote); + OnChange(*changeNotification, std::move(kvStoreSnapshotProxy)); + } else { + OnChange(*changeNotification, nullptr); + } + } else { + std::list insertEntries; + bool result = ReadListFromBuf(data, insertEntries); + if (!result) { + ZLOGE("read insertList from buff filed"); + return errorResult; + } + + std::list updateEntries; + result = ReadListFromBuf(data, updateEntries); + if (!result) { + ZLOGE("read updateList from buff filed"); + return errorResult; + } + + std::list deleteEntries; + result = ReadListFromBuf(data, deleteEntries); + if (!result) { + ZLOGE("read deleteList from buff filed"); + return errorResult; + } + + std::string deviceId = data.ReadString(); + bool isClear = data.ReadBool(); + ChangeNotification changeNotification(insertEntries, updateEntries, deleteEntries, deviceId, isClear); + sptr remote = data.ReadRemoteObject(); + if (remote != nullptr) { + sptr kvStoreSnapshotProxy = iface_cast(remote); + OnChange(changeNotification, std::move(kvStoreSnapshotProxy)); + } else { + ZLOGD("read kvstoreSnapshot is nullptr."); + OnChange(changeNotification, nullptr); + } + } + return 0; + } + default: + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_resultset.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_resultset.cpp new file mode 100755 index 000000000..eb4946ee7 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_resultset.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. + */ + +#define LOG_TAG "KvStoreResultSetProxy" + +#include "ikvstore_resultset.h" +#include "constant.h" +#include "message_parcel.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +enum { + GETCOUNT, + GETPOSITION, + MOVETOFIRST, + MOVETOLAST, + MOVETONEXT, + MOVETOPREVIOUS, + MOVE, + MOVETOPOSITION, + ISFIRST, + ISLAST, + ISBEFOREFIRST, + ISAFTERLAST, + GETENTRY, +}; + +KvStoreResultSetProxy::KvStoreResultSetProxy(const sptr &impl) : IRemoteProxy(impl) +{} + +int KvStoreResultSetProxy::GetCount() +{ + return SendRequest(GETCOUNT); +} + +int KvStoreResultSetProxy::GetPosition() +{ + return SendRequest(GETPOSITION); +} + +bool KvStoreResultSetProxy::MoveToFirst() +{ + return SendRequestRetBool(MOVETOFIRST); +} + +bool KvStoreResultSetProxy::MoveToLast() +{ + return SendRequestRetBool(MOVETOLAST); +} + +bool KvStoreResultSetProxy::MoveToNext() +{ + return SendRequestRetBool(MOVETONEXT); +} + +bool KvStoreResultSetProxy::MoveToPrevious() +{ + return SendRequestRetBool(MOVETOPREVIOUS); +} + +bool KvStoreResultSetProxy::Move(int offset) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreResultSetProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return false; + } + bool ret = data.WriteInt32(offset); + if (!ret) { + return ret; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(MOVE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d, code=%d", error, MOVE); + return false; + } + return reply.ReadBool(); +} + +bool KvStoreResultSetProxy::MoveToPosition(int position) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreResultSetProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return false; + } + bool ret = data.WriteInt32(position); + if (!ret) { + return ret; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(MOVETOPOSITION, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d, code=%d", error, MOVETOPOSITION); + return false; + } + return reply.ReadBool(); +} + +bool KvStoreResultSetProxy::IsFirst() +{ + return SendRequestRetBool(ISFIRST); +} + +bool KvStoreResultSetProxy::IsLast() +{ + return SendRequestRetBool(ISLAST); +} + +bool KvStoreResultSetProxy::IsBeforeFirst() +{ + return SendRequestRetBool(ISBEFOREFIRST); +} + +bool KvStoreResultSetProxy::IsAfterLast() +{ + return SendRequestRetBool(ISAFTERLAST); +} + +Status KvStoreResultSetProxy::GetEntry(Entry &entry) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreResultSetProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + bool ret = reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY); // 800K + if (!ret) { + ZLOGW("set max capacity failed."); + return Status::ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + ZLOGI("start"); + int32_t error = Remote()->SendRequest(GETENTRY, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success(%d)", static_cast(status)); + return status; + } + sptr valueTmp = reply.ReadParcelable(); + if (valueTmp != nullptr) { + entry = *valueTmp; + } + return Status::SUCCESS; +} + +int KvStoreResultSetProxy::SendRequest(uint32_t code) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreResultSetProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return -1; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(code, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d, code=%d", error, code); + return -1; + } + return reply.ReadInt32(); +} + +bool KvStoreResultSetProxy::SendRequestRetBool(uint32_t code) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreResultSetProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return false; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(code, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequestRetBool returned %d, code=%d", error, code); + return false; + } + return reply.ReadBool(); +} +int KvStoreResultSetStub::GetEntryOnRemote(MessageParcel &reply) +{ + Entry entry; + Status ret = GetEntry(entry); + if (!reply.WriteInt32(static_cast(ret)) || + !reply.WriteParcelable(&entry)) { + ZLOGW("ResultSet service side GetEntry fail."); + } + return 0; +} +int KvStoreResultSetStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, + MessageOption &option) +{ + ZLOGD("%u", code); + std::u16string descriptor = KvStoreResultSetStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + switch (code) { + case GETCOUNT: { + int count = GetCount(); + bool ret = reply.WriteInt32(count); + if (!ret) { + ZLOGW("ResultSet service side GetCount fail."); + } + return 0; + } + case GETPOSITION: { + int position = GetPosition(); + bool ret = reply.WriteInt32(position); + if (!ret) { + ZLOGW("ResultSet service side GetPosition fail."); + } + return 0; + } + case MOVETOFIRST: { + bool isFirst = MoveToFirst(); + bool ret = reply.WriteBool(isFirst); + if (!ret) { + ZLOGW("ResultSet service side GetPosition fail."); + } + return 0; + } + case MOVETOLAST: { + bool isLast = MoveToLast(); + bool ret = reply.WriteBool(isLast); + if (!ret) { + ZLOGW("ResultSet service side GetPosition fail."); + } + return 0; + } + case MOVETONEXT: { + bool isNext = MoveToNext(); + bool ret = reply.WriteBool(isNext); + if (!ret) { + ZLOGW("ResultSet service side MoveToNext fail."); + } + return 0; + } + case MOVETOPREVIOUS: { + bool boolRet = MoveToPrevious(); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side MoveToPrevious fail."); + } + return 0; + } + case MOVE: { + uint32_t offset = data.ReadUint32(); + bool boolRet = Move(offset); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side Move fail."); + } + return 0; + } + case MOVETOPOSITION: { + uint32_t position = data.ReadUint32(); + bool boolRet = MoveToPosition(position); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side MoveToPosition fail."); + } + return 0; + } + case ISFIRST: { + bool boolRet = IsFirst(); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side IsFirst fail."); + } + return 0; + } + case ISLAST: { + bool boolRet = IsLast(); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side IsLast fail."); + } + return 0; + } + case ISBEFOREFIRST: { + bool boolRet = IsBeforeFirst(); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side IsBeforeFirst fail."); + } + return 0; + } + case ISAFTERLAST: { + bool boolRet = IsAfterLast(); + bool ret = reply.WriteBool(boolRet); + if (!ret) { + ZLOGW("ResultSet service side IsAfterLast fail."); + } + return 0; + } + case GETENTRY: { + return GetEntryOnRemote(reply); + } + default: { + ZLOGW("OnRemoteRequest default %d", code); + MessageOption mo { MessageOption::TF_SYNC }; + return IPCObjectStub::OnRemoteRequest(code, data, reply, mo); + } + } +} +} // namespace OHOS::DistributedKv diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_single.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_single.cpp new file mode 100755 index 000000000..7d9c8f668 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_single.cpp @@ -0,0 +1,1525 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SingleKvStoreProxy" + +#include "ikvstore_single.h" +#include +#include "constant.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +constexpr SingleKvStoreStub::RequestHandler SingleKvStoreStub::HANDLERS[SINGLE_CMD_LAST]; + +SingleKvStoreProxy::SingleKvStoreProxy(const sptr &impl) : IRemoteProxy(impl) +{} + +Status SingleKvStoreProxy::Put(const Key &key, const Value &value) +{ + ZLOGD("proxy put"); + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set max capacity fail."); + return Status::IPC_ERROR; + } + + int bufferSize = key.RawSize() + value.RawSize(); + if (!data.WriteInt32(bufferSize)) { + ZLOGW("write buffer size failed."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + if (!data.WriteParcelable(&key) || !data.WriteParcelable(&value)) { + ZLOGW("write parcelable failed."); + return Status::IPC_ERROR; + } + int error = Remote()->SendRequest(PUT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed with error code %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); + } + ZLOGI("putting large data."); + std::unique_ptr buffer(new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::ERROR; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + if (!key.WriteToBuffer(cursor, bufferLeftSize) || !value.WriteToBuffer(cursor, bufferLeftSize) || + !data.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write big data to buffer failed"); + return Status::IPC_ERROR; + } + // Parcel before IPC: + // buffer: options | bufferSize + // rawdata: keySize | key | ValueSize | value + int32_t error = Remote()->SendRequest(PUT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Delete(const Key &key) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteParcelable(&key)) { + ZLOGW("write key to parcel fail"); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Get(const Key &key, Value &value) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set max capacity fail."); + return Status::IPC_ERROR; + } + if (!data.WriteParcelable(&key)) { + ZLOGW("write parcel key fail"); + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GET, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success(%d)", static_cast(status)); + return status; + } + + int bufferSize = reply.ReadInt32(); + if (bufferSize < 0) { + ZLOGW("bufferSize < 0(%d)", bufferSize); + return Status::ERROR; + } + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + sptr valueTmp = reply.ReadParcelable(); + if (valueTmp != nullptr) { + value = *valueTmp; + } + return status; + } + ZLOGI("getting big data"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::IPC_ERROR; + } + if (!value.ReadFromBuffer(buffer, bufferSize)) { + ZLOGW("read value from buffer failed"); + return Status::IPC_ERROR; + } + return Status::SUCCESS; +} + +Status SingleKvStoreProxy::SubscribeKvStore(const SubscribeType subscribeType, + sptr observer) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + if (!data.WriteInt32(static_cast(subscribeType)) || + !data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + ZLOGW("write subscribe type or parcel failed."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(SUBSCRIBEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::UnSubscribeKvStore(const SubscribeType subscribeType, + sptr observer) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + if (!data.WriteInt32(static_cast(subscribeType)) || + !data.WriteRemoteObject(observer->AsObject().GetRefPtr())) { + ZLOGW("write subscribe type or parcel failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(UNSUBSCRIBEKVSTORE, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::GetEntries(const Key &prefixKey, std::vector &entries) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY) || !data.WriteParcelable(&prefixKey)) { + ZLOGW("set max capacity or write parcel failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETENTRIES, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + return status; + } + + int replyEntryCount = reply.ReadInt32(); + int bufferSize = reply.ReadInt32(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (int i = 0; i < replyEntryCount; i++) { + sptr entry = reply.ReadParcelable(); + if (entry == nullptr) { + ZLOGW("entry is nullptr"); + entries.clear(); + return Status::IPC_ERROR; + } + entries.push_back(*entry); + } + } else { + ZLOGI("getting large entry set"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (replyEntryCount < 0 || bufferSize < 0 || buffer == nullptr) { + ZLOGW("replyEntryCount(%d) or bufferSize(%d) less than 0, or buffer is nullptr", replyEntryCount, + bufferSize); + return Status::IPC_ERROR; + } + const uint8_t *rawDataCursor = buffer; + int bufferLeftSize = bufferSize; + std::vector entriesTmp = std::vector(replyEntryCount); + for (auto &entry : entriesTmp) { + if (!entry.key.ReadFromBuffer(rawDataCursor, bufferLeftSize) || + !entry.value.ReadFromBuffer(rawDataCursor, bufferLeftSize)) { + ZLOGW("read entry from buffer failed"); + entries.clear(); + return Status::IPC_ERROR; + } + } + entries = std::move(entriesTmp); + } + return Status::SUCCESS; +} + +Status SingleKvStoreProxy::GetEntriesWithQuery(const std::string &query, std::vector &entries) +{ + ZLOGD("begin"); + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set max capacity failed."); + return Status::IPC_ERROR; + } + if (!data.WriteString(query)) { + ZLOGW("set max capacity or write parcel failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETENTRIESWITHQUERY, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + return status; + } + + int replyEntryCount = reply.ReadInt32(); + int bufferSize = reply.ReadInt32(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (int i = 0; i < replyEntryCount; i++) { + sptr entry = reply.ReadParcelable(); + if (entry == nullptr) { + ZLOGW("entry is nullptr"); + entries.clear(); + return Status::IPC_ERROR; + } + entries.push_back(*entry); + } + } else { + ZLOGI("getting large entry set"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (replyEntryCount < 0 || bufferSize < 0 || buffer == nullptr) { + ZLOGW("replyEntryCount(%d) or bufferSize(%d) less than 0, or buffer is nullptr", replyEntryCount, + bufferSize); + return Status::IPC_ERROR; + } + const uint8_t *rawDataCursor = buffer; + int bufferLeftSize = bufferSize; + std::vector entriesTmp = std::vector(replyEntryCount); + for (auto &entry : entriesTmp) { + if (!entry.key.ReadFromBuffer(rawDataCursor, bufferLeftSize) || + !entry.value.ReadFromBuffer(rawDataCursor, bufferLeftSize)) { + ZLOGW("read entry from buffer failed"); + entries.clear(); + return Status::IPC_ERROR; + } + } + entries = std::move(entriesTmp); + } + return Status::SUCCESS; +} + +void SingleKvStoreProxy::GetResultSet(const Key &prefixKey, + std::function)> callback) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteParcelable(&prefixKey)) { + ZLOGW("SendRequest GetResultSet WriteParcel fail."); + callback(Status::IPC_ERROR, nullptr); + return; + } + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETRESULTSET, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + callback(Status::IPC_ERROR, nullptr); + return; + } + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + callback(status, nullptr); + return; + } + sptr remote = reply.ReadRemoteObject(); + if (remote == nullptr) { + callback(status, nullptr); + return; + } + sptr kvstoreResultSetProxy = iface_cast(remote); + callback(status, std::move(kvstoreResultSetProxy)); +} + +void SingleKvStoreProxy::GetResultSetWithQuery(const std::string &query, + std::function)> callback) +{ + ZLOGD("begin"); + MessageParcel data; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteString(query)) { + ZLOGW("SendRequest GetResultSet WriteParcel fail."); + callback(Status::IPC_ERROR, nullptr); + return; + } + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETRESULTSETWITHQUERY, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + callback(Status::IPC_ERROR, nullptr); + return; + } + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + callback(status, nullptr); + return; + } + sptr remote = reply.ReadRemoteObject(); + if (remote == nullptr) { + callback(status, nullptr); + return; + } + sptr kvstoreResultSetProxy = iface_cast(remote); + callback(status, std::move(kvstoreResultSetProxy)); +} + +Status SingleKvStoreProxy::GetCountWithQuery(const std::string &query, int &result) +{ + ZLOGD("begin"); + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY) || + !data.WriteString(query)) { + ZLOGW("set max capacity or write parcel failed."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETCOUNTWITHQUERY, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + return status; + } + + result = reply.ReadInt32(); + return Status::SUCCESS; +} + +Status SingleKvStoreProxy::CloseResultSet(sptr resultSetPtr) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (resultSetPtr == nullptr) { + return Status::INVALID_ARGUMENT; + } + + if (!data.WriteRemoteObject(resultSetPtr->AsObject().GetRefPtr())) { + ZLOGW("Write Strong Parcel fail."); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(CLOSERESULTSET, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Sync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t allowedDelayMs) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + if (!data.WriteStringVector(deviceIdList) || + !data.WriteInt32(static_cast(mode))) { + ZLOGW("SendRequest write parcel failed."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(allowedDelayMs))) { + ZLOGW("sync allowedDelayMs"); + return Status::IPC_ERROR; + } + int32_t error = Remote()->SendRequest(SYNC, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::RemoveDeviceData(const std::string &device) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteString(device)) { + ZLOGW("WriteParcel failed."); + return Status::IPC_ERROR; + } + + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(REMOVEDEVICEDATA, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::RegisterSyncCallback(sptr callback) +{ + if (callback == nullptr) { + ZLOGW("RegisterSyncCallback input is null"); + return Status::INVALID_ARGUMENT; + } + MessageParcel data; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteRemoteObject(callback->AsObject().GetRefPtr())) { + ZLOGW("RegisterSyncCallback write input binder is null"); + return Status::IPC_ERROR; + } + + MessageParcel reply; + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(REGISTERSYNCCALLBACK, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::UnRegisterSyncCallback() +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(UNREGISTERSYNCCALLBACK, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::PutBatch(const std::vector &entries) +{ + ZLOGI("PutBatch begin"); + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set capacity failed."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(entries.size())) { + ZLOGW("write entries size failed."); + return Status::IPC_ERROR; + } + + int64_t bufferSize = 0; + for (const auto &item : entries) { + if (item.key.Size() > Constant::MAX_KEY_LENGTH || item.value.Size() > Constant::MAX_VALUE_LENGTH) { + return Status::INVALID_ARGUMENT; + } + bufferSize += item.key.RawSize() + item.value.RawSize(); + } + if (!data.WriteInt32(bufferSize)) { + ZLOGW("write buffer size failed."); + return Status::IPC_ERROR; + } + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (const auto &item : entries) { + if (!data.WriteParcelable(&item)) { + ZLOGW("write parcel failed."); + return Status::IPC_ERROR; + } + } + MessageOption mo { MessageOption::TF_SYNC }; + if (Remote()->SendRequest(PUTBATCH, data, reply, mo) != 0) { + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); + } + ZLOGI("putting large data."); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("batch size larger than Messageparcel limit.(%" PRId64")", bufferSize); + return Status::INVALID_ARGUMENT; + } + std::unique_ptr buffer(new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::ERROR; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : entries) { + if (!item.key.WriteToBuffer(cursor, bufferLeftSize) || + !item.value.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write to buffer failed."); + return Status::ERROR; + } + } + if (!data.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed"); + return Status::ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(PUTBATCH, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::DeleteBatch(const std::vector &keys) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set capacity failed."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(keys.size())) { + ZLOGW("write keys size failed."); + return Status::IPC_ERROR; + } + for (const auto &item : keys) { + if (keys.size() > Constant::MAX_KEY_LENGTH) { + ZLOGW("Delete key size larger than key size limit"); + return Status::INVALID_ARGUMENT; + } + if (!data.WriteParcelable(&item)) { + ZLOGW("write parcel failed."); + return Status::IPC_ERROR; + } + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(DELETEBATCH, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::StartTransaction() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(STARTTRANSACTION, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Commit() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(COMMIT, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Rollback() +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(ROLLBACK, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest returned %d", error); + return Status::IPC_ERROR; + } + return static_cast(reply.ReadInt32()); +} + +Status SingleKvStoreProxy::Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set max capacity fail."); + return Status::IPC_ERROR; + } + if (!data.WriteInt32(static_cast(cmd))) { + ZLOGW("write cmd failed."); + return Status::IPC_ERROR; + } + if (!data.WriteParcelable(&inputParam)) { + ZLOGW("write parcel fail"); + return Status::IPC_ERROR; + } + + MessageOption mo{MessageOption::TF_SYNC}; + int32_t error = Remote()->SendRequest(CONTROL, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success(%d)", static_cast(status)); + return status; + } + + int bufferSize = reply.ReadInt32(); + if (bufferSize > 0 && bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + output = reply.ReadParcelable(); + } + + return status; +} + +Status SingleKvStoreProxy::SetCapabilityEnabled(bool enabled) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!data.WriteBool(enabled)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(SETCAPABILITYENABLED, data, reply, mo); + + Status status = Status::IPC_ERROR; + if (error == 0) { + status = static_cast(reply.ReadInt32()); + } + return status; +} + +Status SingleKvStoreProxy::SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + + if (!data.WriteStringVector(localLabels)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + + if (!data.WriteStringVector(remoteSupportLabels)) { + ZLOGW("failed to write parcel."); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(SETCAPABILITYRANGE, data, reply, mo); + + Status status = Status::IPC_ERROR; + if (error == 0) { + status = static_cast(reply.ReadInt32()); + } + return status; +} + +Status SingleKvStoreProxy::GetSecurityLevel(SecurityLevel &securityLevel) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(SingleKvStoreProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(SETSECURITLEVEL, data, reply, mo); + + Status status = Status::IPC_ERROR; + if (error == 0) { + status = static_cast(reply.ReadInt32()); + } + if (status == Status::SUCCESS) { + securityLevel = static_cast(reply.ReadInt32()); + } + return status; +} + +int SingleKvStoreStub::PutOnRemote(MessageParcel &data, MessageParcel &reply) +{ + const int bufferSize = data.ReadInt32(); + ZLOGD("bufferSize %d", bufferSize); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + sptr key = data.ReadParcelable(); + sptr value = data.ReadParcelable(); + if (key == nullptr || value == nullptr) { + ZLOGW("nullptr after ipc"); + reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT)); + return 0; + } + Status status = Put(*key, *value); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write status fail"); + return -1; + } + return 0; + } + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(data.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write status fail"); + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + const uint8_t *cursor = buffer; + Key key; + if (!key.ReadFromBuffer(cursor, bufferLeftSize)) { + ZLOGW("read key error."); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + Value value; + if (!value.ReadFromBuffer(cursor, bufferLeftSize)) { + ZLOGW("read value error"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + Status status = Put(key, value); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int SingleKvStoreStub::DeleteOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + Status status = Delete(*key); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int SingleKvStoreStub::GetOnRemote(MessageParcel &data, MessageParcel &reply) +{ + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set reply MessageParcel capacity failed"); + return -1; + } + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + Value value; + Status status = Get(*key, value); + int bufferSize = value.RawSize(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&value)) { + ZLOGW("write value to parcel fail."); + return -1; + } + return 0; + } + ZLOGI("getting large entry"); + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::ILLEGAL_STATE))) { + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(bufferSize)) { + ZLOGW("write bufferSize failed."); + return -1; + } + + uint8_t *cursor = buffer.get(); + if (!value.WriteToBuffer(cursor, bufferLeftSize) || + !reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write bufferSize failed."); + return -1; + } + return 0; +} +int SingleKvStoreStub::SubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + SubscribeType subscribeType = static_cast(data.ReadInt32()); + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr kvStoreObserverProxy = iface_cast(remote); + + Status status = SubscribeKvStore(subscribeType, std::move(kvStoreObserverProxy)); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int SingleKvStoreStub::UnSubscribeKvStoreOnRemote(MessageParcel &data, MessageParcel &reply) +{ + SubscribeType subscribeType = static_cast(data.ReadInt32()); + sptr remote = data.ReadRemoteObject(); + if (remote == nullptr) { + ZLOGW("kvStoreObserverProxy nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + return -1; + } + return 0; + } + sptr kvStoreObserverProxy = iface_cast(remote); + Status status = UnSubscribeKvStore(subscribeType, std::move(kvStoreObserverProxy)); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} +int SingleKvStoreStub::WriteEntriesParcelable(MessageParcel &reply, Status status, + std::vector entries, int bufferSize) +{ + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(entries.size()) || + !reply.WriteInt32(bufferSize)) { + ZLOGW("write status to parcel failed."); + return -1; + } + for (auto const &entry : entries) { + if (!reply.WriteParcelable(&entry)) { + ZLOGW("write entry to parcel failed."); + return -1; + } + } + return 0; +} + +int SingleKvStoreStub::GetTotalEntriesSize(std::vector entries) +{ + int bufferSize = 0; + for (const auto &entry : entries) { + bufferSize += entry.key.RawSize(); + bufferSize += entry.value.RawSize(); + } + return bufferSize; +} +int SingleKvStoreStub::GetEntriesOnRemote(MessageParcel &data, MessageParcel &reply) +{ + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set reply MessageParcel capacity failed"); + return -1; + } + sptr keyPrefix = data.ReadParcelable(); + if (keyPrefix == nullptr) { + ZLOGW("keyPrefix is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + std::vector entries; + Status status = GetEntries(*keyPrefix, entries); + if (status != Status::SUCCESS) { + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; + } + + int bufferSize = GetTotalEntriesSize(entries); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + return WriteEntriesParcelable(reply, status, entries, bufferSize); + } + ZLOGI("getting large entry set"); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("bufferSize %d larger than message parcel limit", bufferSize); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(entries.size()) || + !reply.WriteInt32(bufferSize)) { + ZLOGW("write entry size failed."); + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : entries) { + if (!item.key.WriteToBuffer(cursor, bufferLeftSize) || + !item.value.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write wo buffer failed."); + return -1; + } + } + if (!reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed"); + return -1; + } + return 0; +} +int SingleKvStoreStub::GetEntriesWithQueryOnRemote(MessageParcel &data, MessageParcel &reply) +{ + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set reply MessageParcel capacity failed"); + return -1; + } + std::string query = data.ReadString(); + std::vector entries; + Status status = GetEntriesWithQuery(query, entries); + if (status != Status::SUCCESS) { + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; + } + + int bufferSize = GetTotalEntriesSize(entries); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + return WriteEntriesParcelable(reply, status, entries, bufferSize); + } + ZLOGI("getting large entry set"); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("bufferSize %d larger than message parcel limit", bufferSize); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(entries.size()) || + !reply.WriteInt32(bufferSize)) { + ZLOGW("write entry failed."); + return -1; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : entries) { + if (!item.key.WriteToBuffer(cursor, bufferLeftSize) || + !item.value.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write to buffer failed."); + return -1; + } + } + if (!reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed"); + return -1; + } + return 0; +} +int SingleKvStoreStub::SyncOnRemote(MessageParcel &data, MessageParcel &reply) +{ + std::vector devices; + if (!data.ReadStringVector(&devices) || devices.empty()) { + ZLOGW("SYNC list:%zu", devices.size()); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write sync status fail"); + return -1; + } + return 0; + } + auto mode = static_cast(data.ReadInt32()); + auto allowedDelayMs = static_cast(data.ReadInt32()); + Status status = Sync(devices, mode, allowedDelayMs); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write sync status fail"); + return -1; + } + return 0; +} +int SingleKvStoreStub::GetResultSetOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("keyPrefix is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write getResultSet status fail"); + return -1; + } + return 0; + } + + sptr proxyTmp; + Status statusTmp; + auto fun = [&](Status status, sptr proxy) { + statusTmp = status; + proxyTmp = std::move(proxy); + }; + GetResultSet(*key, fun); + if (!reply.WriteInt32(static_cast(statusTmp))) { + ZLOGW("write statusTmp fail"); + return -1; + } + if (statusTmp == Status::SUCCESS && proxyTmp != nullptr) { + if (!reply.WriteRemoteObject(proxyTmp->AsObject().GetRefPtr())) { + ZLOGW("write strong fail."); + return -1; + } + } + return 0; +} +int SingleKvStoreStub::GetResultSetWithQueryOnRemote(MessageParcel &data, MessageParcel &reply) +{ + std::string query = data.ReadString(); + sptr proxyTmp; + Status statusTmp; + auto fun = [&](Status status, sptr proxy) { + statusTmp = status; + proxyTmp = std::move(proxy); + }; + GetResultSetWithQuery(query, fun); + if (!reply.WriteInt32(static_cast(statusTmp))) { + ZLOGW("write statusTmp fail"); + return -1; + } + if (proxyTmp != nullptr) { + if (!reply.WriteRemoteObject(proxyTmp->AsObject().GetRefPtr())) { + ZLOGW("write strong fail."); + return -1; + } + } else { + ZLOGW("service side snapshot proxy is nullptr"); + if (!reply.WriteParcelable(nullptr)) { + ZLOGW("write nullptr fail."); + return -1; + } + } + return 0; +} +int SingleKvStoreStub::PutBatchOnRemote(MessageParcel &data, MessageParcel &reply) +{ + if (!data.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set batch size failed."); + return -1; + } + int len = data.ReadInt32(); + if (len < 0) { + ZLOGW("invalid status. len %d", len); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write to parcel failed."); + return -1; + } + return 0; + } + const int bufferSize = data.ReadInt32(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + std::vector entries; + for (int i = 0; i < len; i++) { + sptr entry = data.ReadParcelable(); + if (entry == nullptr) { + ZLOGW("putbatch got null entry pointer"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write to parcel failed."); + return -1; + } + return 0; + } + entries.push_back(*entry); + } + Status status = PutBatch(entries); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write putbatch failed."); + return -1; + } + return 0; + } + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(data.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + const uint8_t *cursor = buffer; + std::vector entries; + Entry entry; + for (int i = 0; i < len; i++) { + bool success = entry.key.ReadFromBuffer(cursor, bufferLeftSize); + success = success && entry.value.ReadFromBuffer(cursor, bufferLeftSize); + entries.push_back(std::move(entry)); + if (!success) { + ZLOGW("get key or value failed"); + if (!reply.WriteInt32(static_cast(Status::IPC_ERROR))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; + } + } + Status status = PutBatch(entries); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write putbatch big failed."); + return -1; + } + return 0; +} +int SingleKvStoreStub::DeleteBatchOnRemote(MessageParcel &data, MessageParcel &reply) +{ + int len = data.ReadInt32(); + if (len < 0) { + ZLOGW("len %d invalid after ipc", len); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; + } + std::vector keys; + for (int i = 0; i < len; i++) { + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key nullptr"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; + } + keys.push_back(*key); + } + Status status = DeleteBatch(keys); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write delete failed."); + return -1; + } + return 0; +} +int SingleKvStoreStub::ControlOnRemote(MessageParcel &data, MessageParcel &reply) +{ + KvControlCmd cmd = static_cast(data.ReadInt32()); + sptr inputParam = data.ReadParcelable(); + if (inputParam == nullptr) { + ZLOGW("inputParam is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + sptr output = nullptr; + Status status = Control(cmd, *inputParam, output); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write control failed."); + return -1; + } + if ((output != nullptr) && (output->RawSize() < Constant::SWITCH_RAW_DATA_SIZE)) { + if (!reply.WriteInt32(output->RawSize())) { + ZLOGW("write bufferSize failed."); + return -1; + } + if (!reply.WriteParcelable(output)) { + ZLOGW("write control output failed"); + return -1; + } + } else { + if (!reply.WriteInt32(0)) { + ZLOGW("write bufferSize 0 failed."); + return -1; + } + } + return 0; +} + +int SingleKvStoreStub::OnSecurityLevelRequest(MessageParcel& data, MessageParcel &reply) +{ + SecurityLevel securityLevel = SecurityLevel::NO_LABEL; + auto status = GetSecurityLevel(securityLevel); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("Get SecurityLevel ipc failed."); + return -1; + } + if (!reply.WriteInt32(static_cast(securityLevel))) { + ZLOGW("Get SecurityLevel ipc failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::OnCapabilityRangeRequest(MessageParcel &data, MessageParcel &reply) +{ + std::vector locals; + if (!data.ReadStringVector(&locals)) { + ZLOGW("read local capability range ipc failed."); + return -1; + } + + std::vector remotes; + if (!data.ReadStringVector(&remotes)) { + ZLOGW("remote local capability range ipc failed."); + return -1; + } + + Status ret = SetCapabilityRange(locals, remotes); + if (!reply.WriteInt32(static_cast(ret))) { + ZLOGW("set capability range ipc failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::GetCountWithQueryOnRemote(MessageParcel &data, MessageParcel &reply) +{ + std::string query = data.ReadString(); + int result; + Status status = GetCountWithQuery(query, result); + if (status != Status::SUCCESS) { + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; + } + if (!reply.WriteInt32(static_cast(status)) || !reply.WriteInt32(result)) { + ZLOGW("write result and status failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::CloseResultSetOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr obj = data.ReadRemoteObject(); + if (obj == nullptr) { + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + sptr kvStoreResultSetProxy = iface_cast(obj); + Status status = CloseResultSet(kvStoreResultSetProxy); + if (!reply.WriteInt32(static_cast(status))) { + return -1; + } + return 0; +} + +int SingleKvStoreStub::RemoveDeviceDataOnRemote(MessageParcel &data, MessageParcel &reply) +{ + std::string deviceId = data.ReadString(); + if (deviceId.empty()) { + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + ZLOGW("write remove data status fail."); + return -1; + } + return 0; + } + + Status status = RemoveDeviceData(deviceId); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write status failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::RegisterSyncCallbackOnRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr obj = data.ReadRemoteObject(); + if (obj == nullptr) { + ZLOGW("kvStoreSyncCallbackProxy nullptr after ipc"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + sptr kvStoreSyncCallbackProxy = iface_cast(obj); + Status status = RegisterSyncCallback(kvStoreSyncCallbackProxy); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("kvStoreSyncCallbackProxy write status fail"); + return -1; + } + return 0; +} + +int SingleKvStoreStub::UnRegisterSyncCallbackOnRemote(MessageParcel &data, MessageParcel &reply) +{ + Status status = UnRegisterSyncCallback(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write status fail."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::StartTransactionOnRemote(MessageParcel &data, MessageParcel &reply) +{ + Status status = StartTransaction(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write starttransaction failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::CommitOnRemote(MessageParcel &data, MessageParcel &reply) +{ + Status status = Commit(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write commit failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::RollbackOnRemote(MessageParcel &data, MessageParcel &reply) +{ + Status status = Rollback(); + if (!reply.WriteInt32(static_cast(status))) { + ZLOGW("write rollback failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::OnCapabilityEnableRequest(MessageParcel &data, MessageParcel &reply) +{ + if (!reply.WriteInt32(static_cast(SetCapabilityEnabled(data.ReadBool())))) { + ZLOGW("set capability enable ipc failed."); + return -1; + } + return 0; +} + +int SingleKvStoreStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, + MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = SingleKvStoreStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + if (code >= 0 && code < SINGLE_CMD_LAST) { + return (this->*HANDLERS[code])(data, reply); + } else { + MessageOption mo { MessageOption::TF_SYNC }; + return IPCObjectStub::OnRemoteRequest(code, data, reply, mo); + } +} +} // namespace OHOS::DistributedKv diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_snapshot.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_snapshot.cpp new file mode 100755 index 000000000..ed6745386 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_snapshot.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreSnapshotImplProxy" + +#include "ikvstore_snapshot.h" +#include "constant.h" +#include "log_print.h" +#include "message_parcel.h" + +namespace OHOS { +namespace DistributedKv { +enum { + GETENTRIES, + GETKEYS, + GET, +}; + +KvStoreSnapshotImplProxy::KvStoreSnapshotImplProxy(const sptr &impl) + : IRemoteProxy(impl) +{} + +KvStoreSnapshotImplProxy::~KvStoreSnapshotImplProxy() +{} + +void KvStoreSnapshotImplProxy::GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &entries, const Key &key)> callback) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreSnapshotImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + std::vector entries; + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + ZLOGW("set max capacity failed"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + if (!data.WriteParcelable(&prefixKey)) { + ZLOGW("write prefix failed"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + if (!data.WriteParcelable(&nextKey)) { + ZLOGW("write nextkey failed"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + MessageOption mo { MessageOption::TF_SYNC }; + // struct of returned reply: + // buffer: | status | entryLength | rawdatasize | (sptr)nextkey | + // rawData: ( | keyLen | key | valueLen | value | ){entryLength} + int32_t error = Remote()->SendRequest(GETENTRIES, data, reply, mo); + + if (error != 0) { + ZLOGW("Transact failed"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success, which is %d", static_cast(status)); + callback(status, entries, Key()); + return; + } + int replyEntryCount = reply.ReadInt32(); + int bufferSize = reply.ReadInt32(); + sptr keyTmp = reply.ReadParcelable(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (int i = 0; i < replyEntryCount; i++) { + sptr entry = reply.ReadParcelable(); + if (entry == nullptr) { + ZLOGW("entry is nullptr"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + entries.push_back(*entry); + } + } else { + ZLOGI("getting large entry set"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (replyEntryCount < 0 || bufferSize < 0 || buffer == nullptr) { + ZLOGW("replyEntryCount(%d) or bufferSize(%d) less than 0, or buffer is nullptr", replyEntryCount, + bufferSize); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + const uint8_t *rawDataCursor = buffer; + int bufferLeftSize = bufferSize; + entries = std::vector(replyEntryCount); + for (auto &entry : entries) { + if (!entry.key.ReadFromBuffer(rawDataCursor, bufferLeftSize) || + !entry.value.ReadFromBuffer(rawDataCursor, bufferLeftSize)) { + ZLOGW("read key or value from buffer failed"); + callback(Status::IPC_ERROR, entries, Key()); + return; + } + } + } + if (keyTmp != nullptr) { + callback(status, entries, *keyTmp); + } else { + callback(status, entries, Key()); + } +} + +void KvStoreSnapshotImplProxy::GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &keys, const Key &key)> callback) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreSnapshotImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + std::vector keyList; + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + if (!data.WriteParcelable(&prefixKey)) { + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + if (!data.WriteParcelable(&nextKey)) { + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + MessageOption mo { MessageOption::TF_SYNC }; + int32_t error = Remote()->SendRequest(GETKEYS, data, reply, mo); + if (error != 0) { + ZLOGW("Transact failed"); + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success, which is %d", static_cast(status)); + callback(status, keyList, Key()); + return; + } + int replyKeyCount = reply.ReadInt32(); + int bufferSize = reply.ReadInt32(); + sptr keyTmp = reply.ReadParcelable(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + for (int i = 0; i < replyKeyCount; i++) { + sptr keyInner = reply.ReadParcelable(); + if (keyInner == nullptr) { + ZLOGW("keyInner is nullptr"); + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + keyList.push_back(*keyInner); + } + } else { + ZLOGI("getting large key set"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (replyKeyCount < 0 || bufferSize < 0 || buffer == nullptr) { + ZLOGW("replyKeyCount(%d) or bufferSize(%d) less than 0, or buffer is nullptr", replyKeyCount, bufferSize); + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + const uint8_t *rawDataCursor = buffer; + int bufferLeftSize = bufferSize; + keyList = std::vector(replyKeyCount); + for (auto &key : keyList) { + if (!key.ReadFromBuffer(rawDataCursor, bufferLeftSize)) { + ZLOGW("read key from buffer failed"); + callback(Status::IPC_ERROR, keyList, Key()); + return; + } + } + } + if (keyTmp != nullptr) { + callback(status, keyList, *keyTmp); + } else { + callback(status, keyList, Key()); + } +} + +Status KvStoreSnapshotImplProxy::Get(const Key &key, Value &value) +{ + MessageParcel data, reply; + if (!data.WriteInterfaceToken(KvStoreSnapshotImplProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return Status::IPC_ERROR; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + return Status::IPC_ERROR; + } + if (!data.WriteParcelable(&key)) { + return Status::IPC_ERROR; + } + + MessageOption mo { MessageOption::TF_SYNC }; + ZLOGI("start"); + int32_t error = Remote()->SendRequest(GET, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error is %d", error); + return Status::IPC_ERROR; + } + Status status = static_cast(reply.ReadInt32()); + if (status != Status::SUCCESS) { + ZLOGW("status not success(%d)", static_cast(status)); + return status; + } + + int bufferSize = reply.ReadInt32(); + if (bufferSize < 0) { + ZLOGW("bufferSize < 0(%d)", bufferSize); + return Status::ERROR; + } + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + sptr valueTmp = reply.ReadParcelable(); + if (valueTmp != nullptr) { + value = *valueTmp; + } + return status; + } + ZLOGI("getting big data"); + // this memory is managed by MassageParcel, DO NOT free here + const uint8_t *buffer = reinterpret_cast(reply.ReadRawData(bufferSize)); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + return Status::IPC_ERROR; + } + if (!value.ReadFromBuffer(buffer, bufferSize)) { + ZLOGW("read value from buffer failed"); + return Status::IPC_ERROR; + } + return Status::SUCCESS; +} + +int32_t KvStoreSnapshotImplStub::GetTotalEntriesSize(std::vector entryList) +{ + int bufferSize = 0; + for (const auto &item : entryList) { + bufferSize += item.key.RawSize() + item.value.RawSize(); + } + return bufferSize; +} +int32_t KvStoreSnapshotImplStub::WriteEntriesParcelable(MessageParcel &reply, Status statusTmp, + std::vector entryList, int bufferSize, Key nxtKey) +{ + if (!reply.WriteInt32(static_cast(statusTmp)) || + !reply.WriteInt32(entryList.size()) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write entry to parcel failed."); + return -1; + } + for (const auto &item : entryList) { + if (!reply.WriteParcelable(&item)) { + ZLOGW("write item to parcel failed."); + return -1; + } + } + return 0; +} +int32_t KvStoreSnapshotImplStub::GetTotalkeysSize(std::vector keyList) +{ + int bufferSize = 0; + for (const auto &key : keyList) { + bufferSize += key.RawSize(); + } + return bufferSize; +} +int32_t KvStoreSnapshotImplStub::WritekeysParcelable(MessageParcel &reply, Status statusTmp, + std::vector keyList, int bufferSize, Key nxtKey) +{ + if (!reply.WriteInt32(static_cast(statusTmp)) || + !reply.WriteInt32(keyList.size()) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write buffer size failed."); + return -1; + } + for (const auto &item : keyList) { + if (!reply.WriteParcelable(&item)) { + ZLOGW("write item failed."); + return -1; + } + } + return 0; +} + +int32_t KvStoreSnapshotImplStub::GetEntriesOnRemote(MessageParcel &data, MessageParcel &reply) +{ + // struct of returned reply: + // buffer: | status | entryLength | rawdatasize | (sptr)nextkey | + // rawData: ( | keyLen | key | valueLen | value | ){entryLength} + sptr keyPrefix = data.ReadParcelable(); + sptr nextKey = data.ReadParcelable(); + if (keyPrefix == nullptr) { + ZLOGW("keyPrefix is null. return."); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + if (nextKey == nullptr) { + ZLOGW("nextKey is null. return."); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + return 0; + } + std::vector entryList; + Key nxtKey; + Status statusTmp; + GetEntries(*keyPrefix, *nextKey, [&](Status status, const std::vector &entries, const Key &key) { + statusTmp = status; + entryList = std::move(entries); + nxtKey = key; + }); + int bufferSize = GetTotalEntriesSize(entryList); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + return WriteEntriesParcelable(reply, statusTmp, entryList, bufferSize, nxtKey); + } + ZLOGI("getting large entry set"); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("bufferSize %d larger than message parcel limit", bufferSize); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::ERROR))) { + return -1; + } + return 0; + } + + if (!reply.WriteInt32(static_cast(statusTmp)) || + !reply.WriteInt32(entryList.size()) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write entries failed."); + return -1; + } + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &item : entryList) { + if (!item.key.WriteToBuffer(cursor, bufferLeftSize) || + !item.value.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write to buffer failed"); + return -1; + } + } + if (!reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed"); + return -1; + } + return 0; +} +int32_t KvStoreSnapshotImplStub::GetKeysRemote(MessageParcel &data, MessageParcel &reply) +{ + // struct of returned reply: + // buffer: | status | keyListLength | rawdatasize | (sptr)nextkey | + // rawData: ( | keyLen | key | ){keyListLength} + sptr keyPrefix = data.ReadParcelable(); + sptr nextKey = data.ReadParcelable(); + if (keyPrefix == nullptr) { + ZLOGW("keyPrefix is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + if (!reply.WriteParcelable(nullptr)) { + return -1; + } + return 0; + } + if (nextKey == nullptr) { + ZLOGW("nextKey is null. return."); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + if (!reply.WriteParcelable(nullptr)) { + return -1; + } + return 0; + } + std::vector keyList; + Key nxtKey; + Status statusTmp; + GetKeys(*keyPrefix, *nextKey, [&](Status status, const std::vector &keys, const Key &key) { + statusTmp = status; + keyList = std::move(keys); + nxtKey = key; + }); + int bufferSize = GetTotalkeysSize(keyList); + + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + return WritekeysParcelable(reply, statusTmp, keyList, bufferSize, nxtKey); + } + ZLOGI("getting large key set"); + if (bufferSize > static_cast(reply.GetRawDataCapacity())) { + ZLOGW("bufferSize %d larger than message parcel limit", bufferSize); + if (!reply.WriteInt32(static_cast(Status::ERROR)) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write status failed."); + return -1; + } + return 0; + } + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("alloc memory failed(buffer is null). perhaps low on memory."); + if (!reply.WriteInt32(static_cast(Status::ERROR)) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write nxtkey failed."); + return -1; + } + return 0; + } + if (!reply.WriteInt32(static_cast(statusTmp)) || + !reply.WriteInt32(keyList.size()) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&nxtKey)) { + ZLOGW("write meta failed."); + return -1; + } + + int bufferLeftSize = bufferSize; + uint8_t *cursor = buffer.get(); + for (const auto &key : keyList) { + if (!key.WriteToBuffer(cursor, bufferLeftSize)) { + ZLOGW("write to buffer failed."); + return -1; + } + } + if (!reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed"); + return -1; + } + return 0; +} +int32_t KvStoreSnapshotImplStub::GetRemote(MessageParcel &data, MessageParcel &reply) +{ + sptr key = data.ReadParcelable(); + if (key == nullptr) { + ZLOGW("key is null"); + if (!reply.WriteInt32(static_cast(Status::INVALID_ARGUMENT))) { + return -1; + } + if (!reply.WriteInt32(0)) { + return -1; + } + return 0; + } + Value value; + Status status = Get(*key, value); + + int bufferSize = value.RawSize(); + if (bufferSize < Constant::SWITCH_RAW_DATA_SIZE) { + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(bufferSize) || + !reply.WriteParcelable(&value)) { + ZLOGW("write meta failed."); + return -1; + } + return 0; + } + ZLOGI("getting large entry"); + std::unique_ptr buffer( + new uint8_t[bufferSize], [](uint8_t *ptr) { delete[] ptr; }); + if (buffer == nullptr) { + ZLOGW("buffer is null"); + if (!reply.WriteInt32(static_cast(Status::ILLEGAL_STATE)) || + !reply.WriteInt32(0)) { + ZLOGW("write state failed."); + return -1; + } + return 0; + } + int bufferLeftSize = bufferSize; + if (!reply.WriteInt32(static_cast(status)) || + !reply.WriteInt32(bufferSize)) { + ZLOGW("write bufferSize failed."); + return -1; + } + + uint8_t *cursor = buffer.get(); + if (!value.WriteToBuffer(cursor, bufferLeftSize) || + !reply.WriteRawData(buffer.get(), bufferSize)) { + ZLOGW("write rawData failed."); + return -1; + } + return 0; +} + +int32_t KvStoreSnapshotImplStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + ZLOGD("%d", code); + std::u16string descriptor = KvStoreSnapshotImplStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + if (!reply.SetMaxCapacity(Constant::MAX_IPC_CAPACITY)) { + return -1; + } + switch (code) { + case GETENTRIES: { + return GetEntriesOnRemote(data, reply); + } + case GETKEYS: { + return GetKeysRemote(data, reply); + } + case GET: { + return GetRemote(data, reply); + } + default: + if (!reply.WriteInt32((int32_t)Status::ERROR)) { + return -1; + } + return 0; + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_sync_callback.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_sync_callback.cpp new file mode 100644 index 000000000..c2ede3608 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_sync_callback.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. + */ + +#define LOG_TAG "KvStoreSyncCallbackProxy" + +#include "ikvstore_sync_callback.h" +#include +#include +#include "log_print.h" +#include "message_parcel.h" + +namespace OHOS { +namespace DistributedKv { +enum { + SYNCCOMPLETED, +}; +constexpr int32_t MAX_DEVICES = 4096; +KvStoreSyncCallbackProxy::KvStoreSyncCallbackProxy(const sptr &impl) + : IRemoteProxy(impl) +{} + +void KvStoreSyncCallbackProxy::SyncCompleted(const std::map &results) +{ + MessageParcel data; + MessageParcel reply; + if (!data.WriteInterfaceToken(KvStoreSyncCallbackProxy::GetDescriptor())) { + ZLOGE("write descriptor failed"); + return; + } + if (!data.WriteInt32(static_cast(results.size()))) { + ZLOGW("write results size error."); + return; + } + for (auto const &[k, v] : results) { + if (!data.WriteString(k) || + !data.WriteInt32(static_cast(v))) { + ZLOGW("write results error."); + return; + } + } + MessageOption mo { MessageOption::TF_SYNC }; + int error = Remote()->SendRequest(SYNCCOMPLETED, data, reply, mo); + if (error != 0) { + ZLOGW("SendRequest failed, error %d", error); + } +} + +int32_t KvStoreSyncCallbackStub::OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) +{ + std::u16string descriptor = KvStoreSyncCallbackStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + ZLOGE("local descriptor is not equal to remote"); + return -1; + } + switch (code) { + case SYNCCOMPLETED: { + std::map results; + int32_t size = data.ReadInt32(); + if (size < 0 || size > MAX_DEVICES) { + ZLOGW("size < 0(%d)", size); + return 0; + } + for (int32_t i = 0; i < size; i++) { + results.insert(std::pair(data.ReadString(), + static_cast(data.ReadInt32()))); + } + SyncCompleted(results); + return 0; + } + default: + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.cpp new file mode 100755 index 000000000..ac0e8e71c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreClient" + +#include "constant.h" +#include "dds_trace.h" +#include "kvstore_client.h" +#include "kvstore_observer_client.h" +#include "kvstore_snapshot_client.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreClient::KvStoreClient(sptr kvStoreProxy, const std::string &storeId) + : kvStoreProxy_(std::move(kvStoreProxy)), storeId_(storeId) +{ + ZLOGI("construct"); +} + +KvStoreClient::~KvStoreClient() +{ + ZLOGI("destruct"); +} + +StoreId KvStoreClient::GetStoreId() const +{ + StoreId storeId; + storeId.storeId = storeId_; + return storeId; +} + +void KvStoreClient::GetKvStoreSnapshot(std::shared_ptr observer, + std::function)> callback) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + Status statusTmp = Status::SERVER_UNAVAILABLE; + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + callback(statusTmp, nullptr); + return; + } + + sptr kvStoreObserverClient = new KvStoreObserverClient(GetStoreId(), + SubscribeType::SUBSCRIBE_TYPE_ALL, observer, KvStoreType::MULTI_VERSION); + + sptr snapshotProxyTmp; + auto snapshotCallbackFunction = [&](Status status, sptr snapshotProxy) { + statusTmp = status; + snapshotProxyTmp = snapshotProxy; + }; + kvStoreProxy_->GetKvStoreSnapshot(kvStoreObserverClient, snapshotCallbackFunction); + if (statusTmp != Status::SUCCESS) { + ZLOGE("return error: %d.", static_cast(statusTmp)); + callback(statusTmp, nullptr); + return; + } + + if (snapshotProxyTmp == nullptr) { + ZLOGE("snapshotProxyTmp is nullptr."); + callback(statusTmp, nullptr); + return; + } + + ZLOGD("success."); + callback(statusTmp, std::make_unique(std::move(snapshotProxyTmp))); +} + +Status KvStoreClient::ReleaseKvStoreSnapshot(std::unique_ptr kvStoreSnapshotPtr) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + if (kvStoreSnapshotPtr == nullptr) { + ZLOGE("kvstoresnapshot is nullptr."); + return Status::INVALID_ARGUMENT; + } + + KvStoreSnapshotClient *kvStoreSnapshotClient = + reinterpret_cast(kvStoreSnapshotPtr.release()); + sptr snapshotProxyTmp = kvStoreSnapshotClient->GetkvStoreSnapshotProxy(); + Status status = kvStoreProxy_->ReleaseKvStoreSnapshot(std::move(snapshotProxyTmp)); + delete kvStoreSnapshotClient; + kvStoreSnapshotClient = nullptr; + ZLOGI("return: %d.", static_cast(status)); + return status; +} + +Status KvStoreClient::Put(const Key &key, const Value &value) +{ + ZLOGD("key: %zu value: %zu.", key.Size(), value.Size()); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH || + value.Size() > Constant::MAX_VALUE_LENGTH) { + ZLOGE("invalid key or value."); + return Status::INVALID_ARGUMENT; + } + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Put(key, value); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::PutBatch(const std::vector &entries) +{ + ZLOGI("entry size: %zu", entries.size()); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (entries.size() > Constant::MAX_BATCH_SIZE) { + ZLOGE("batch size must less than 128."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->PutBatch(entries); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::Delete(const Key &key) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Delete(key); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::DeleteBatch(const std::vector &keys) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (keys.size() > Constant::MAX_BATCH_SIZE) { + ZLOGE("batch size must less than 128."); + return Status::INVALID_ARGUMENT; + } + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->DeleteBatch(keys); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::Clear() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Clear(); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::StartTransaction() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->StartTransaction(); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::Commit() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Commit(); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::Rollback() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Rollback(); + } + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status KvStoreClient::SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (observer == nullptr) { + ZLOGW("return INVALID_ARGUMENT."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(observerMapMutex_); + // change this to map.contains() after c++20 + if (registeredObservers_.count(observer.get()) == 1) { + ZLOGW("return STORE_ALREADY_SUBSCRIBE."); + return Status::STORE_ALREADY_SUBSCRIBE; + } + + // remove storeId after remove SubscribeKvStore function in manager. currently reserve for convenience. + sptr ipcObserver = + new (std::nothrow) KvStoreObserverClient(GetStoreId(), subscribeType, observer, KvStoreType::MULTI_VERSION); + if (ipcObserver == nullptr) { + ZLOGW("new KvStoreObserverClient failed"); + return Status::ERROR; + } + Status status = kvStoreProxy_->SubscribeKvStore(subscribeType, ipcObserver); + if (status == Status::SUCCESS) { + registeredObservers_.emplace(observer.get(), ipcObserver); + } + return status; +} + +Status KvStoreClient::UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (observer == nullptr) { + ZLOGW("return INVALID_ARGUMENT."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(observerMapMutex_); + auto it = registeredObservers_.find(observer.get()); + if (it == registeredObservers_.end()) { + ZLOGW("return STORE_NOT_SUBSCRIBE."); + return Status::STORE_NOT_SUBSCRIBE; + } + Status status = kvStoreProxy_->UnSubscribeKvStore(subscribeType, it->second); + if (status == Status::SUCCESS) { + registeredObservers_.erase(it); + } + return status; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.h new file mode 100644 index 000000000..01d81a615 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.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 KVSTORE_CLIENT_H +#define KVSTORE_CLIENT_H + +#include +#include "ikvstore.h" +#include "kvstore.h" +#include "kvstore_service_death_notifier.h" +#include "kvstore_snapshot.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreClient final : public KvStore { +public: + explicit KvStoreClient(sptr kvStoreProxy, const std::string &storeId); + + ~KvStoreClient(); + + StoreId GetStoreId() const override; + + void GetKvStoreSnapshot(std::shared_ptr observer, + std::function)> callback) const override; + + Status ReleaseKvStoreSnapshot(std::unique_ptr kvStoreSnapshotPtr) override; + + Status Put(const Key &key, const Value &value) override; + + Status PutBatch(const std::vector &entries) override; + + Status Delete(const Key &key) override; + + Status DeleteBatch(const std::vector &keys) override; + + Status Clear() override; + + Status StartTransaction() override; + + Status Commit() override; + + Status Rollback() override; + + Status SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) override; + + Status UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) override; + +private: + sptr kvStoreProxy_; + std::map> registeredObservers_; + std::mutex observerMapMutex_; + std::string storeId_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.cpp new file mode 100755 index 000000000..0d2706bc4 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreClientDeathObserver" + +#include "kvstore_client_death_observer.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreClientDeathObserver::KvStoreClientDeathObserver() +{ + ZLOGI("this client death observer"); +} + +KvStoreClientDeathObserver::~KvStoreClientDeathObserver() +{ + ZLOGI("destructor this client death observer"); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.h new file mode 100644 index 000000000..88f1db226 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_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 KVSTORE_CLIENT_DEATH_OBSERVER_H +#define KVSTORE_CLIENT_DEATH_OBSERVER_H + +#include "ikvstore_client_death_observer.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreClientDeathObserver : public KvStoreClientDeathObserverStub { +public: + KvStoreClientDeathObserver(); + + virtual ~KvStoreClientDeathObserver(); +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_CLIENT_DEATH_OBSERVER_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.cpp new file mode 100644 index 000000000..2587be1e2 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.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. + */ + +#define LOG_TAG "KvStoreDeathRecipientImpl" + +#include "kvstore_death_recipient_impl.h" + +#include +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreDeathRecipientImpl::KvStoreDeathRecipientImpl(std::shared_ptr kvStoreDeathRecipient) + : kvStoreDeathRecipient_(std::move(kvStoreDeathRecipient)) +{ + ZLOGI("constructor"); +} + +KvStoreDeathRecipientImpl::~KvStoreDeathRecipientImpl() +{ + ZLOGI("destructor"); +} + +void KvStoreDeathRecipientImpl::OnRemoteDied() +{ + ZLOGI("OnRemoteDied"); + if (kvStoreDeathRecipient_ != nullptr) { + ZLOGI("call client"); + kvStoreDeathRecipient_->OnRemoteDied(); + } +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.h new file mode 100644 index 000000000..5e334cde6 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.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 KVSTORE_DEATH_RECIPIENT_IMPL_H +#define KVSTORE_DEATH_RECIPIENT_IMPL_H + +#include +#include "kvstore_death_recipient.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreDeathRecipientImpl { +public: + explicit KvStoreDeathRecipientImpl(std::shared_ptr kvStoreDeathRecipient); + ~KvStoreDeathRecipientImpl(); + void OnRemoteDied(); +private: + std::shared_ptr kvStoreDeathRecipient_; +friend struct KvStoreDeathRecipientImplCompare; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_DEATH_RECIPIENT_IMPL_H \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.cpp new file mode 100755 index 000000000..b1e951e59 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.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. + */ + +#define LOG_TAG "KvStoreObserverClient" + +#include "kvstore_observer_client.h" +#include "kvstore_snapshot_client.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreObserverClient::KvStoreObserverClient(const StoreId &storeId, SubscribeType subscribeType, + std::shared_ptr kvStoreObserver, KvStoreType type) + : storeId_(storeId), subscribeType_(subscribeType), kvStoreObserver_(kvStoreObserver), type_(type) +{ + ZLOGI("start"); +} + +KvStoreObserverClient::~KvStoreObserverClient() +{ + ZLOGI("start"); +} + +void KvStoreObserverClient::OnChange(const ChangeNotification &changeNotification, sptr snapshot) +{ + ZLOGI("start"); + if (kvStoreObserver_ != nullptr) { + if (type_ == KvStoreType::SINGLE_VERSION) { + ZLOGI("SINGLE_VERSION start"); + kvStoreObserver_->OnChange(changeNotification); + } else { + ZLOGI("MULTI_VERSION start"); + kvStoreObserver_->OnChange(changeNotification, + std::make_unique(std::move(snapshot))); + } + } +} + +const StoreId &KvStoreObserverClient::GetStoreId() const +{ + return storeId_; +} + +const SubscribeType &KvStoreObserverClient::GetSubscribeType() const +{ + return subscribeType_; +} + +const std::shared_ptr KvStoreObserverClient::GetKvStoreObserver() const +{ + return kvStoreObserver_; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.h new file mode 100644 index 000000000..6e242912a --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.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 KVSTORE_OBSERVER_CLIENT_H +#define KVSTORE_OBSERVER_CLIENT_H + +#include +#include "change_notification.h" +#include "ikvstore_observer.h" +#include "ikvstore_snapshot.h" +#include "kvstore_observer.h" +#include "refbase.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreObserverClient : public KvStoreObserverStub { +public: + KvStoreObserverClient(const StoreId &storeId, SubscribeType subscribeType, + std::shared_ptr kvStoreObserver, KvStoreType type); + + ~KvStoreObserverClient(); + + void OnChange(const ChangeNotification &changeNotification, sptr snapshot) override; + + const StoreId &GetStoreId() const; + + const SubscribeType &GetSubscribeType() const; + + const std::shared_ptr GetKvStoreObserver() const; +private: + static const int MAX_TRY_COUNT = 10; + + StoreId storeId_; + + SubscribeType subscribeType_; + + // client is responsible for free it when call UnSubscribeKvStore. + std::shared_ptr kvStoreObserver_; + KvStoreType type_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_OBSERVER_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_nb_impl.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_nb_impl.h new file mode 100644 index 000000000..0b66a8284 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_nb_impl.h @@ -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. + */ + +#ifndef KVSTORE_OBSERVER_IMPL_H +#define KVSTORE_OBSERVER_IMPL_H + +#include +#include "app_types.h" +#include "kv_store_observer.h" +#include "log_print.h" + +namespace OHOS { +namespace AppDistributedKv { +class KvStoreObserverNbImpl : public DistributedDB::KvStoreObserver { +public: + KvStoreObserverNbImpl(AppKvStoreObserver *appKvStoreObserver, const SubscribeType &subscribeType) + { + appKvStoreObserver_ = appKvStoreObserver; + subscribeType_ = subscribeType; + } + + virtual void OnChange(const DistributedDB::KvStoreChangedData &data) + { + if (appKvStoreObserver_ == nullptr) { + ZLOGE("appKvStoreObserver_ is nullptr."); + return; + } + std::list insertList = data.GetEntriesInserted(); + std::list updateList = data.GetEntriesUpdated(); + std::list deletedList = data.GetEntriesDeleted(); + + std::list insertListTmp; + std::list updateListTmp; + std::list deletedListTmp; + + for (const auto &entry : insertList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + insertListTmp.push_back(tmpEntry); + } + + for (const auto &entry : updateList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + updateListTmp.push_back(tmpEntry); + } + + for (const auto &entry : deletedList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + deletedListTmp.push_back(tmpEntry); + } + + AppChangeNotification changeNotification(insertListTmp, updateListTmp, deletedListTmp, std::string(), false); + appKvStoreObserver_->OnChange(changeNotification); + } + + virtual ~KvStoreObserverNbImpl() + {} +private: + AppKvStoreObserver *appKvStoreObserver_; + SubscribeType subscribeType_; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // APP_KV_STORE_OBSERVER_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.cpp new file mode 100755 index 000000000..61c4707aa --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreResultSetClient" + +#include "kvstore_resultset_client.h" +#include "dds_trace.h" + +namespace OHOS::DistributedKv { +KvStoreResultSetClient::KvStoreResultSetClient(sptr kvStoreProxy) + :kvStoreResultSetProxy_(kvStoreProxy) +{} + +int KvStoreResultSetClient::GetCount() const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + return kvStoreResultSetProxy_->GetCount(); +} + +int KvStoreResultSetClient::GetPosition() const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->GetPosition(); +} + +bool KvStoreResultSetClient::MoveToFirst() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->MoveToFirst(); +} + +bool KvStoreResultSetClient::MoveToLast() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->MoveToLast(); +} + +bool KvStoreResultSetClient::MoveToNext() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->MoveToNext(); +} + +bool KvStoreResultSetClient::MoveToPrevious() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->MoveToPrevious(); +} + +bool KvStoreResultSetClient::Move(int offset) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->Move(offset); +} + +bool KvStoreResultSetClient::MoveToPosition(int position) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + return kvStoreResultSetProxy_->MoveToPosition(position); +} + +bool KvStoreResultSetClient::IsFirst() const +{ + return kvStoreResultSetProxy_->IsFirst(); +} + +bool KvStoreResultSetClient::IsLast() const +{ + return kvStoreResultSetProxy_->IsLast(); +} + +bool KvStoreResultSetClient::IsBeforeFirst() const +{ + return kvStoreResultSetProxy_->IsBeforeFirst(); +} + +bool KvStoreResultSetClient::IsAfterLast() const +{ + return kvStoreResultSetProxy_->IsAfterLast(); +} + +Status KvStoreResultSetClient::GetEntry(Entry &entry) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + return kvStoreResultSetProxy_->GetEntry(entry); +} + +sptr KvStoreResultSetClient::GetKvStoreResultSetProxy() const +{ + return kvStoreResultSetProxy_; +} +} // namespace OHOS::DistributedKv \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.h new file mode 100644 index 000000000..eb3d40978 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.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 DISTRIBUTEDDATAMGR_KVSTORE_RESULTSET_CLIENT_H +#define DISTRIBUTEDDATAMGR_KVSTORE_RESULTSET_CLIENT_H + +#include "ikvstore_resultset.h" +#include "kvstore_result_set.h" + +namespace OHOS::DistributedKv { +class KvStoreResultSetClient : public KvStoreResultSet { +public: + explicit KvStoreResultSetClient(sptr kvStoreProxy); + + ~KvStoreResultSetClient() + {} + + int GetCount() const override; + + int GetPosition() const override; + + bool MoveToFirst() override; + + bool MoveToLast() override; + + bool MoveToNext() override; + + bool MoveToPrevious() override; + + bool Move(int offset) override; + + bool MoveToPosition(int position) override; + + bool IsFirst() const override; + + bool IsLast() const override; + + bool IsBeforeFirst() const override; + + bool IsAfterLast() const override; + + Status GetEntry(Entry &entry) const override; + + sptr GetKvStoreResultSetProxy() const; +private: + sptr kvStoreResultSetProxy_; +}; +} // namespace OHOS::DistributedKv +#endif // DISTRIBUTEDDATAMGR_KVSTORE_RESULTSET_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.cpp new file mode 100755 index 000000000..c0f239c76 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreServiceDeathNotifier" + +#include "kvstore_service_death_notifier.h" +#include "if_system_ability_manager.h" +#include "ipc_skeleton.h" +#include "iservice_registry.h" +#include "kvstore_client_death_observer.h" +#include "log_print.h" +#include "refbase.h" +#include "system_ability_definition.h" + +namespace OHOS { +namespace DistributedKv { +AppId KvStoreServiceDeathNotifier::appId_; +std::mutex KvStoreServiceDeathNotifier::watchMutex_; +sptr KvStoreServiceDeathNotifier::kvDataServiceProxy_; +sptr KvStoreServiceDeathNotifier::deathRecipientPtr_; +sptr KvStoreServiceDeathNotifier::clientDeathObserverPtr_; +std::set, KvStoreDeathRecipientImplCompare> + KvStoreServiceDeathNotifier::serviceDeathWatchers_; + +void KvStoreServiceDeathNotifier::SetAppId(const AppId &appId) +{ + appId_ = appId; +} + +sptr KvStoreServiceDeathNotifier::GetDistributedKvDataService() +{ + ZLOGD("begin."); + std::lock_guard lg(watchMutex_); + if (kvDataServiceProxy_ != nullptr) { + return kvDataServiceProxy_; + } + + ZLOGI("create remote proxy."); + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + ZLOGE("get samgr fail."); + return nullptr; + } + + auto remote = samgr->CheckSystemAbility(DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID); + kvDataServiceProxy_ = iface_cast(remote); + if (kvDataServiceProxy_ == nullptr) { + ZLOGE("initialize proxy failed."); + return nullptr; + } + + if (deathRecipientPtr_ == nullptr) { + deathRecipientPtr_ = new (std::nothrow) KvStoreDeathRecipient(); + if (deathRecipientPtr_ == nullptr) { + ZLOGW("new KvStoreDeathRecipient failed"); + return nullptr; + } + } + if ((remote->IsProxyObject()) && (!remote->AddDeathRecipient(deathRecipientPtr_))) { + ZLOGE("failed to add death recipient."); + } + + RegisterClientDeathObserver(); + + return kvDataServiceProxy_; +} + +void KvStoreServiceDeathNotifier::RegisterClientDeathObserver() +{ + if (kvDataServiceProxy_ == nullptr) { + return; + } + if (clientDeathObserverPtr_ == nullptr) { + clientDeathObserverPtr_ = new (std::nothrow) KvStoreClientDeathObserver(); + } + if (clientDeathObserverPtr_ == nullptr) { + ZLOGW("new KvStoreClientDeathObserver failed"); + return; + } + kvDataServiceProxy_->RegisterClientDeathObserver(appId_, clientDeathObserverPtr_); +} + +void KvStoreServiceDeathNotifier::AddServiceDeathWatcher(std::shared_ptr watcher) +{ + std::lock_guard lg(watchMutex_); + auto ret = serviceDeathWatchers_.insert(watcher); + if (ret.second) { + ZLOGI("success set size: %zu", serviceDeathWatchers_.size()); + } else { + ZLOGE("failed set size: %zu", serviceDeathWatchers_.size()); + } +} + +void KvStoreServiceDeathNotifier::RemoveServiceDeathWatcher(std::shared_ptr watcher) +{ + std::lock_guard lg(watchMutex_); + auto it = serviceDeathWatchers_.find(watcher); + if (it != serviceDeathWatchers_.end()) { + serviceDeathWatchers_.erase(it); + ZLOGI("find & erase set size: %zu", serviceDeathWatchers_.size()); + } else { + ZLOGE("no found set size: %zu", serviceDeathWatchers_.size()); + } +} + +void KvStoreServiceDeathNotifier::KvStoreDeathRecipient::OnRemoteDied(const wptr &remote) +{ + ZLOGW("DistributedDataMgrService died."); + // Need to do this with the lock held + std::lock_guard lg(watchMutex_); + kvDataServiceProxy_ = nullptr; + ZLOGI("watcher set size: %zu", serviceDeathWatchers_.size()); + for (const auto &watcher : serviceDeathWatchers_) { + std::thread th = std::thread([watcher]() { + watcher->OnRemoteDied(); + }); + th.detach(); + } +} + +KvStoreServiceDeathNotifier::KvStoreDeathRecipient::KvStoreDeathRecipient() +{ + ZLOGI("constructor."); +} + +KvStoreServiceDeathNotifier::KvStoreDeathRecipient::~KvStoreDeathRecipient() +{ + ZLOGI("destructor."); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.h new file mode 100644 index 000000000..34ec75712 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.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 KVSTORE_SERVICE_DEATH_NOTIFIER_H +#define KVSTORE_SERVICE_DEATH_NOTIFIER_H + +#include +#include +#include +#include "ikvstore_data_service.h" +#include "iremote_object.h" +#include "kvstore_death_recipient_impl.h" +#include "refbase.h" + +namespace OHOS { +namespace DistributedKv { +struct KvStoreDeathRecipientImplCompare { + bool operator()(const std::shared_ptr lhs, + const std::shared_ptr rhs) const + { + return lhs->kvStoreDeathRecipient_ < rhs->kvStoreDeathRecipient_; + } +}; + +class KvStoreServiceDeathNotifier final { +public: + KvStoreServiceDeathNotifier() = delete; + ~KvStoreServiceDeathNotifier() = delete; + // get DistributedKvDataService proxy object. + static sptr GetDistributedKvDataService(); + // temporarily used, should get in service side from binder. + static void SetAppId(const AppId &appId); + // add watcher for server die msg. + static void AddServiceDeathWatcher(std::shared_ptr watcher); + // remove watcher for server die msg. + static void RemoveServiceDeathWatcher(std::shared_ptr watcher); +private: + class KvStoreDeathRecipient : public IRemoteObject::DeathRecipient { + public: + KvStoreDeathRecipient(); + + virtual ~KvStoreDeathRecipient(); + + void OnRemoteDied(const wptr &remote) override; + }; + + // add watcher for server die msg. + static void RegisterClientDeathObserver(); + static AppId appId_; + // lock for kvDataServiceProxy_ and serviceDeathWatchers_. + static std::mutex watchMutex_; + static sptr kvDataServiceProxy_; + static sptr deathRecipientPtr_; + static sptr clientDeathObserverPtr_; + // set of watchers for server die msg. + static std::set, KvStoreDeathRecipientImplCompare> serviceDeathWatchers_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_SERVICE_DEATH_NOTIFIER_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.cpp new file mode 100644 index 000000000..0a64baff0 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreSnapshotClient" + +#include "constant.h" +#include "dds_trace.h" +#include "kvstore_snapshot_client.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreSnapshotClient::KvStoreSnapshotClient() : kvStoreSnapshotProxy_(nullptr) +{} + +KvStoreSnapshotClient::KvStoreSnapshotClient(sptr kvStoreSnapshotProxy) + : kvStoreSnapshotProxy_(std::move(kvStoreSnapshotProxy)) +{ + ZLOGI("construct"); +} + +KvStoreSnapshotClient::~KvStoreSnapshotClient() +{ + ZLOGI("destruct"); +} + +void KvStoreSnapshotClient::GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::vector entries; + std::vector keyData = Constant::TrimCopy>(prefixKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid prefixKey."); + callback(Status::INVALID_ARGUMENT, entries, nextKey); + return; + } + keyData = Constant::TrimCopy>(nextKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid nextKey."); + callback(Status::INVALID_ARGUMENT, entries, nextKey); + return; + } + if (kvStoreSnapshotProxy_ != nullptr) { + kvStoreSnapshotProxy_->GetEntries(prefixKey, nextKey, callback); + return; + } + + ZLOGE("snapshot proxy is nullptr."); + callback(Status::SERVER_UNAVAILABLE, entries, nextKey); +} + +void KvStoreSnapshotClient::GetEntries(const Key &prefixKey, std::function &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::vector allEntries; + std::vector keyData = Constant::TrimCopy>(prefixKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid prefixKey."); + callback(Status::INVALID_ARGUMENT, allEntries); + return; + } + if (kvStoreSnapshotProxy_ == nullptr) { + ZLOGE("snapshot proxy is nullptr."); + callback(Status::SERVER_UNAVAILABLE, allEntries); + return; + } + Key startKey = ""; + Status allStatus = Status::ERROR; + do { + kvStoreSnapshotProxy_->GetEntries(prefixKey, startKey, + [&allStatus, &allEntries, &startKey](Status stat, std::vector &entries, Key next) { + allStatus = stat; + if (stat != Status::SUCCESS) { + return; + } + for (const auto &entry : entries) { + allEntries.push_back(entry); + } + startKey = next; + if (entries.empty()) { + startKey = ""; + } + }); + } while (allStatus == Status::SUCCESS && startKey.ToString() != ""); + if (allStatus != Status::SUCCESS) { + ZLOGW("Error occurs during GetEntries."); + allEntries.clear(); + } + callback(allStatus, allEntries); +} + +void KvStoreSnapshotClient::GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::vector keys; + std::vector keyData = Constant::TrimCopy>(prefixKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid prefixKey."); + callback(Status::INVALID_ARGUMENT, keys, nextKey); + return; + } + keyData = Constant::TrimCopy>(nextKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid nextKey."); + callback(Status::INVALID_ARGUMENT, keys, nextKey); + return; + } + if (kvStoreSnapshotProxy_ != nullptr) { + kvStoreSnapshotProxy_->GetKeys(prefixKey, nextKey, callback); + return; + } + ZLOGE("snapshot proxy is nullptr."); + callback(Status::SERVER_UNAVAILABLE, keys, nextKey); +} + +void KvStoreSnapshotClient::GetKeys(const Key &prefixKey, std::function &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::vector allKeys; + std::vector keyData = Constant::TrimCopy>(prefixKey.Data()); + if (keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid prefixKey."); + callback(Status::INVALID_ARGUMENT, allKeys); + return; + } + if (kvStoreSnapshotProxy_ == nullptr) { + ZLOGE("snapshot proxy is nullptr."); + callback(Status::SERVER_UNAVAILABLE, allKeys); + return; + } + Key startKey = ""; + Status allStatus = Status::ERROR; + do { + kvStoreSnapshotProxy_->GetKeys(prefixKey, startKey, [&](Status stat, std::vector &keys, Key next) { + allStatus = stat; + if (stat != Status::SUCCESS) { + return; + } + for (const auto &key : keys) { + allKeys.push_back(key); + } + startKey = next; + if (keys.empty()) { + startKey = ""; + } + }); + } while (allStatus == Status::SUCCESS && startKey.ToString() != ""); + if (allStatus != Status::SUCCESS) { + ZLOGW("Error occurs during GetKeys."); + allKeys.clear(); + } + callback(allStatus, allKeys); +} + +Status KvStoreSnapshotClient::Get(const Key &key, Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreSnapshotProxy_ != nullptr) { + return kvStoreSnapshotProxy_->Get(key, value); + } + ZLOGE("snapshot proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +sptr KvStoreSnapshotClient::GetkvStoreSnapshotProxy() +{ + return kvStoreSnapshotProxy_; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.h new file mode 100644 index 000000000..fc1fd6d20 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.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 KVSTORE_SNAPSHOT_CLIENT_H +#define KVSTORE_SNAPSHOT_CLIENT_H + +#include "ikvstore_snapshot.h" +#include "kvstore_service_death_notifier.h" +#include "kvstore_snapshot.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreSnapshotClient final : public KvStoreSnapshot { +public: + KvStoreSnapshotClient(); + + explicit KvStoreSnapshotClient(sptr kvStoreSnapshotProxy); + + ~KvStoreSnapshotClient(); + + void GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) override; + + void GetEntries(const Key &prefixKey, std::function &)> callback) override; + + void GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) override; + + void GetKeys(const Key &prefixKey, std::function &)> callback) override; + + Status Get(const Key &key, Value &value) override; + + sptr GetkvStoreSnapshotProxy(); +private: + // use shared_ptr here to free pointer when reference count is 0. + sptr kvStoreSnapshotProxy_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_SNAPSHOT_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.cpp new file mode 100755 index 000000000..9800daadb --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreSyncCallbackClient" + +#include "kvstore_sync_callback_client.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreSyncCallbackClient::KvStoreSyncCallbackClient(std::shared_ptr kvStoreSyncCallback) + : kvStoreSyncCallback_(kvStoreSyncCallback) +{} + +KvStoreSyncCallbackClient::~KvStoreSyncCallbackClient() +{} + +void KvStoreSyncCallbackClient::SyncCompleted(const std::map &results) +{ + if (kvStoreSyncCallback_ != nullptr) { + kvStoreSyncCallback_->SyncCompleted(results); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.h new file mode 100644 index 000000000..c8ba5fa86 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.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 KVSTORE_SYNC_CALLBACK_CLIENT_H +#define KVSTORE_SYNC_CALLBACK_CLIENT_H + +#include "ikvstore_sync_callback.h" +#include "kvstore_sync_callback.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreSyncCallbackClient : public KvStoreSyncCallbackStub { +public: + explicit KvStoreSyncCallbackClient(std::shared_ptr kvStoreSyncCallback); + + virtual ~KvStoreSyncCallbackClient(); + + void SyncCompleted(const std::map &results) override; + +private: + std::shared_ptr kvStoreSyncCallback_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_SYNC_CALLBACK_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.cpp b/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.cpp new file mode 100755 index 000000000..b89be66ee --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SingleKvStoreClient" + +#include "single_kvstore_client.h" +#include "constant.h" +#include "dds_trace.h" +#include "kvstore_observer_client.h" +#include "kvstore_resultset_client.h" +#include "kvstore_sync_callback_client.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +SingleKvStoreClient::SingleKvStoreClient(sptr kvStoreProxy, const std::string &storeId) + : kvStoreProxy_(kvStoreProxy), storeId_(storeId) +{} + +StoreId SingleKvStoreClient::GetStoreId() const +{ + StoreId storeId; + storeId.storeId = storeId_; + return storeId; +} + +Status SingleKvStoreClient::GetEntries(const Key &prefixKey, std::vector &entries) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + + return kvStoreProxy_->GetEntries(prefixKey, entries); +} + +Status SingleKvStoreClient::GetEntriesWithQuery(const std::string &query, std::vector &entries) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + ZLOGD("Cpp client GetEntriesWithQuery"); + return kvStoreProxy_->GetEntriesWithQuery(query, entries); +} + +Status SingleKvStoreClient::GetEntriesWithQuery(const DataQuery &query, std::vector &entries) const +{ + return GetEntriesWithQuery(query.ToString(), entries); +} + +void SingleKvStoreClient::GetResultSet(const Key &prefixKey, + std::function)> callback) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + Status statusTmp = Status::SERVER_UNAVAILABLE; + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + callback(statusTmp, nullptr); + return; + } + sptr resultSetTmp; + auto callFun = [&](Status status, sptr proxy) { + statusTmp = status; + resultSetTmp = proxy; + }; + kvStoreProxy_->GetResultSet(prefixKey, callFun); + if (statusTmp != Status::SUCCESS) { + ZLOGE("return error: %d.", static_cast(statusTmp)); + callback(statusTmp, nullptr); + return; + } + + if (resultSetTmp == nullptr) { + ZLOGE("resultSetTmp is nullptr."); + callback(statusTmp, nullptr); + return; + } + callback(statusTmp, std::make_unique(std::move(resultSetTmp))); +} + +void SingleKvStoreClient::GetResultSetWithQuery(const std::string &query, + std::function)> callback) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + Status statusTmp = Status::SERVER_UNAVAILABLE; + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + callback(statusTmp, nullptr); + return; + } + + ZLOGD("Cpp client GetResultSetWithQuery"); + sptr resultSetTmp; + auto callFun = [&](Status status, sptr proxy) { + statusTmp = status; + resultSetTmp = proxy; + }; + kvStoreProxy_->GetResultSetWithQuery(query, callFun); + if (statusTmp != Status::SUCCESS) { + ZLOGE("return error: %d.", static_cast(statusTmp)); + callback(statusTmp, nullptr); + return; + } + + if (resultSetTmp == nullptr) { + ZLOGE("resultSetTmp is nullptr."); + callback(statusTmp, nullptr); + return; + } + callback(statusTmp, std::make_unique(std::move(resultSetTmp))); + ZLOGE("GetResultSetWithQuery"); +} + +void SingleKvStoreClient::GetResultSetWithQuery(const DataQuery &query, + std::function)> callback) const +{ + GetResultSetWithQuery(query.ToString(), callback); +} + +Status SingleKvStoreClient::CloseResultSet(std::unique_ptr resultSet) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (resultSet == nullptr) { + ZLOGE("resultSet is nullptr."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + auto resultSetClient = reinterpret_cast(resultSet.release()); + return kvStoreProxy_->CloseResultSet(resultSetClient->GetKvStoreResultSetProxy()); +} + +Status SingleKvStoreClient::GetCountWithQuery(const std::string &query, int &result) const +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + ZLOGD("Cpp client GetCountWithQuery"); + return kvStoreProxy_->GetCountWithQuery(query, result); +} + +Status SingleKvStoreClient::GetCountWithQuery(const DataQuery &query, int &result) const +{ + return GetCountWithQuery(query.ToString(), result); +} + +Status SingleKvStoreClient::Sync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t allowedDelayMs) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + if (deviceIdList.empty()) { + ZLOGW("deviceIdList is empty."); + return Status::INVALID_ARGUMENT; + } + return kvStoreProxy_->Sync(deviceIdList, mode, allowedDelayMs); +} + +Status SingleKvStoreClient::RemoveDeviceData(const std::string &device) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + if (device.empty()) { + ZLOGW("device is empty."); + return Status::INVALID_ARGUMENT; + } + return kvStoreProxy_->RemoveDeviceData(device); +} + +Status SingleKvStoreClient::Delete(const Key &key) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + return kvStoreProxy_->Delete(key); +} + +Status SingleKvStoreClient::Put(const Key &key, const Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + ZLOGI("key: %zu value: %zu.", key.Size(), value.Size()); + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH || + value.Size() > Constant::MAX_VALUE_LENGTH) { + ZLOGE("invalid key or value."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + return kvStoreProxy_->Put(key, value); +} + +Status SingleKvStoreClient::Get(const Key &key, Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ == nullptr) { + ZLOGE("kvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; + } + return kvStoreProxy_->Get(key, value); +} + +Status SingleKvStoreClient::SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (observer == nullptr) { + ZLOGW("return INVALID_ARGUMENT."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(observerMapMutex_); + // change this to map.contains() after c++20 + if (registeredObservers_.count(observer.get()) == 1) { + ZLOGW("return STORE_ALREADY_SUBSCRIBE."); + return Status::STORE_ALREADY_SUBSCRIBE; + } + // remove storeId after remove SubscribeKvStore function in manager. currently reserve for convenience. + sptr ipcObserver = + new (std::nothrow) KvStoreObserverClient(GetStoreId(), subscribeType, observer, + KvStoreType::SINGLE_VERSION); + if (ipcObserver == nullptr) { + ZLOGW("new KvStoreObserverClient failed"); + return Status::ERROR; + } + Status status = kvStoreProxy_->SubscribeKvStore(subscribeType, ipcObserver); + if (status == Status::SUCCESS) { + const auto temp = registeredObservers_.insert({observer.get(), ipcObserver}); + if (!temp.second) { + ZLOGW("local insert error"); + return Status::ERROR; + } + } + return status; +} + +Status SingleKvStoreClient::UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (observer == nullptr) { + ZLOGW("return INVALID_ARGUMENT."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(observerMapMutex_); + auto it = registeredObservers_.find(observer.get()); + if (it == registeredObservers_.end()) { + ZLOGW(" STORE NOT SUBSCRIBE."); + return Status::STORE_NOT_SUBSCRIBE; + } + Status status = kvStoreProxy_->UnSubscribeKvStore(subscribeType, it->second); + if (status == Status::SUCCESS) { + registeredObservers_.erase(it); + } else { + ZLOGW("single unSubscribe failed code=%d.", static_cast(status)); + } + return status; +} + +Status SingleKvStoreClient::RegisterSyncCallback(std::shared_ptr callback) +{ + ZLOGI("begin."); + if (callback == nullptr) { + ZLOGW("return INVALID_ARGUMENT."); + return Status::INVALID_ARGUMENT; + } + // remove storeId after remove SubscribeKvStore function in manager. currently reserve for convenience. + sptr ipcCallback = + new (std::nothrow) KvStoreSyncCallbackClient(callback); + if (ipcCallback == nullptr) { + ZLOGW("new KvStoreSyncCallbackClient failed"); + return Status::ERROR; + } + return kvStoreProxy_->RegisterSyncCallback(ipcCallback); +} + +Status SingleKvStoreClient::UnRegisterSyncCallback() +{ + ZLOGI("begin."); + return kvStoreProxy_->UnRegisterSyncCallback(); +} + +Status SingleKvStoreClient::PutBatch(const std::vector &entries) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + ZLOGI("entry size: %zu", entries.size()); + if (entries.size() > Constant::MAX_BATCH_SIZE) { + ZLOGE("batch size must less than 128."); + return Status::INVALID_ARGUMENT; + } + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->PutBatch(entries); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::DeleteBatch(const std::vector &keys) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (keys.size() > Constant::MAX_BATCH_SIZE) { + ZLOGE("batch size must less than 128."); + return Status::INVALID_ARGUMENT; + } + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->DeleteBatch(keys); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::StartTransaction() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->StartTransaction(); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::Commit() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Commit(); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::Rollback() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__), true); + + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Rollback(); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::SetSyncParam(const KvSyncParam &syncParam) +{ + KvParam input(TransferTypeToByteArray(syncParam)); + sptr output = nullptr; + return Control(KvControlCmd::SET_SYNC_PARAM, input, output); +} + +Status SingleKvStoreClient::GetSyncParam(KvSyncParam &syncParam) +{ + KvParam inputEmpty; + sptr output = nullptr; + Status ret = Control(KvControlCmd::GET_SYNC_PARAM, inputEmpty, output); + if (ret != Status::SUCCESS) { + return ret; + } + if ((output != nullptr) && (output->Size() == sizeof(syncParam))) { + syncParam = TransferByteArrayToType(output->Data()); + return Status::SUCCESS; + } + return Status::ERROR; +} + +Status SingleKvStoreClient::Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) +{ + ZLOGI("begin."); + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->Control(cmd, inputParam, output); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} +Status SingleKvStoreClient::SetCapabilityEnabled(bool enabled) const +{ + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->SetCapabilityEnabled(enabled); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) const +{ + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->SetCapabilityRange(localLabels, remoteSupportLabels); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} + +Status SingleKvStoreClient::GetSecurityLevel(SecurityLevel &securityLevel) const +{ + if (kvStoreProxy_ != nullptr) { + return kvStoreProxy_->GetSecurityLevel(securityLevel); + } + ZLOGE("singleKvstore proxy is nullptr."); + return Status::SERVER_UNAVAILABLE; +} +} // namespace OHOS::DistributedKv diff --git a/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.h b/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.h new file mode 100755 index 000000000..ab8034d8e --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISTRIBUTEDDATAMGR2_SINGLE_KVSTORE_CLIENT_H +#define DISTRIBUTEDDATAMGR2_SINGLE_KVSTORE_CLIENT_H + +#include "data_query.h" +#include "ikvstore_single.h" +#include "single_kvstore.h" + +namespace OHOS::DistributedKv { +class SingleKvStoreClient : public SingleKvStore { +public: + explicit SingleKvStoreClient(sptr kvStoreProxy, const std::string &storeId); + + ~SingleKvStoreClient() + {} + + StoreId GetStoreId() const override; + + Status GetEntries(const Key &prefixKey, std::vector &entries) const override; + + Status GetEntriesWithQuery(const std::string &query, std::vector &entries) const override; + + Status GetEntriesWithQuery(const DataQuery &query, std::vector &entries) const override; + + void GetResultSet(const Key &prefixKey, + std::function)> callback) const override; + + void GetResultSetWithQuery(const std::string &query, + std::function)> callback) const override; + + void GetResultSetWithQuery(const DataQuery &query, + std::function)> callback) const override; + + Status CloseResultSet(std::unique_ptr resultSet) override; + + Status GetCountWithQuery(const std::string &query, int &result) const override; + + Status GetCountWithQuery(const DataQuery &query, int &result) const override; + + Status Sync(const std::vector &deviceIdList, const SyncMode &mode, uint32_t allowedDelayMs) override; + + Status RemoveDeviceData(const std::string &device) override; + + Status Delete(const Key &key) override; + + Status Put(const Key &key, const Value &value) override; + + Status Get(const Key &key, Value &value) override; + + Status SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) override; + + Status UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) override; + + Status RegisterSyncCallback(std::shared_ptr callback) override; + + Status UnRegisterSyncCallback() override; + + Status PutBatch(const std::vector &entries) override; + + Status DeleteBatch(const std::vector &keys) override; + + Status StartTransaction() override; + + Status Commit() override; + + Status Rollback() override; + + Status SetSyncParam(const KvSyncParam &syncParam) override; + + Status GetSyncParam(KvSyncParam &syncParam) override; + Status SetCapabilityEnabled(bool enabled) const override; + Status SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) const override; + + Status GetSecurityLevel(SecurityLevel &securityLevel) const override; +protected: + Status Control(KvControlCmd cmd, const KvParam &inputParam, sptr &outputParam) override; + +private: + sptr kvStoreProxy_; + std::map> registeredObservers_; + std::mutex observerMapMutex_; + std::string storeId_; +}; +} // namespace OHOS::DistributedKv +#endif // DISTRIBUTEDDATAMGR2_SINGLE_KVSTORE_CLIENT_H diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/BUILD.gn b/frameworks/innerkitsimpl/distributeddatafwk/test/BUILD.gn new file mode 100755 index 000000000..c27e9d8cb --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/BUILD.gn @@ -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. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddatafwk" + +############################################################################### +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ + "../include/", + "../../../../interfaces/innerkits/distributeddata/", + + # TEMP MODIFICATION FOR PMS + "../../../../services/distributeddataservice/app/include", + + # for ipc_core interfaces. + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + ] +} + +ohos_unittest("DistributedKvDataManagerTest") { + module_out_path = module_output_path + + sources = [ "unittest/distributed_kv_data_manager_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("DistributedKvDataManagerEncryptTest") { + module_out_path = module_output_path + + sources = [ "unittest/distributed_kv_data_manager_encrypt_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreClientTest") { + module_out_path = module_output_path + + sources = [ + "unittest/kvstore_client_test.cpp", + "unittest/single_kvstore_client_query_test.cpp", + "unittest/single_kvstore_client_test.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreSnapshotClientTest") { + module_out_path = module_output_path + + sources = [ "unittest/kvstore_snapshot_client_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("LocalSubscribeStoreTest") { + module_out_path = module_output_path + + sources = [ "unittest/local_subscribe_store_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("BlobTest") { + module_out_path = module_output_path + + sources = [ "unittest/blob_test.cpp" ] + + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +############################################################################### +config("app_test_config") { + visibility = [ ":*" ] + + include_dirs = [ + "../include/", + "../../../../interfaces/innerkits/app_distributeddata/include", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + ] +} + +ohos_unittest("AppDistributedKvStoreTest") { + module_out_path = module_output_path + + sources = [ "unittest/app_distributed_kv_store_test.cpp" ] + + configs = [ ":app_test_config" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/:app_distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + ] +} + +ohos_unittest("AppDistributedKvDataManagerTest") { + module_out_path = module_output_path + + sources = [ "unittest/app_distributed_kv_data_manager_test.cpp" ] + + configs = [ ":app_test_config" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/:app_distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + ] +} + +ohos_unittest("AppBlobTest") { + module_out_path = module_output_path + + sources = [ "unittest/app_blob_test.cpp" ] + + configs = [ ":app_test_config" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/:app_distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + ] +} + +ohos_unittest("AppConflictTest") { + module_out_path = module_output_path + + sources = [ "unittest/app_conflict_test.cpp" ] + + configs = [ ":app_test_config" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/:app_distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//third_party/googletest:gtest_main", + ] +} + +group("unittest") { + testonly = true + + deps = [] + + deps += [ + ":AppBlobTest", + ":AppConflictTest", + ":AppDistributedKvDataManagerTest", + ":AppDistributedKvStoreTest", + ":BlobTest", + ":DistributedKvDataManagerEncryptTest", + ":DistributedKvDataManagerTest", + ":KvStoreClientTest", + ":KvStoreSnapshotClientTest", + ":LocalSubscribeStoreTest", + ] +} +############################################################################### diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_blob_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_blob_test.cpp new file mode 100755 index 000000000..42c41d8e6 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_blob_test.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "app_types.h" +using namespace testing::ext; +using namespace OHOS::AppDistributedKv; + +class AppBlobTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void AppBlobTest::SetUpTestCase(void) +{} + +void AppBlobTest::TearDownTestCase(void) +{} + +void AppBlobTest::SetUp(void) +{} + +void AppBlobTest::TearDown(void) +{} + +/** + * @tc.name: AppBlobSize001 + * @tc.desc: Construct a Blob and check its size. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobSize001, TestSize.Level0) +{ + AppBlob blob1; + EXPECT_EQ(blob1.Size(), (size_t)0); + AppBlob blob2 = "1234567890"; + EXPECT_EQ(blob2.Size(), (size_t)10); + AppBlob blob3("12345"); + EXPECT_EQ(blob3.Size(), (size_t)5); + std::string strTmp = "123"; + const char *chr = strTmp.c_str(); + AppBlob blob4(chr); + EXPECT_EQ(blob4.Size(), (size_t)3); + std::vector vec = {'1', '2', '3', '4'}; + AppBlob blob5(vec); + EXPECT_EQ(blob5.Size(), (size_t)4); + const char *chr1 = strTmp.c_str(); + AppBlob blob6(chr1, strlen(chr1)); + EXPECT_EQ(blob6.Size(), (size_t)3); +} + +/** + * @tc.name: AppBlobEmpty001 + * @tc.desc: Construct a Blob and check its empty. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobEmpty001, TestSize.Level0) +{ + AppBlob blob1; + EXPECT_EQ(blob1.Empty(), true); + AppBlob blob2 = "1234567890"; + EXPECT_EQ(blob2.Empty(), false); + AppBlob blob3("12345"); + EXPECT_EQ(blob3.Empty(), false); + std::string strTmp = "123"; + const char *chr = strTmp.c_str(); + AppBlob blob4(chr); + EXPECT_EQ(blob4.Empty(), false); + std::vector vec = {'1', '2', '3', '4'}; + AppBlob blob5(vec); + EXPECT_EQ(blob5.Empty(), false); + const char *chr1 = strTmp.c_str(); + AppBlob blob6(chr1, strlen(chr1)); + EXPECT_EQ(blob6.Empty(), false); +} + +/** + * @tc.name: AppBlobCompare001 + * @tc.desc: Construct a Blob and check its StartsWith function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobCompare001, TestSize.Level0) +{ + AppBlob blob1 = "1234567890"; + AppBlob blob2("12345"); + EXPECT_EQ(blob1.Compare(blob2), 1); + EXPECT_EQ(blob2.Compare(blob1), -1); + AppBlob blob3("12345"); + EXPECT_EQ(blob2.Compare(blob3), 0); +} + +/** + * @tc.name: AppBlobData001 + * @tc.desc: Construct a Blob and check its Data function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobData001, TestSize.Level0) +{ + std::vector result = {'1', '2', '3', '4'}; + AppBlob blob1("1234"); + EXPECT_EQ(blob1.Data(), result); + std::vector result2 = {'1', '2', '3', '4', '5'}; + AppBlob blob2("12345"); + EXPECT_EQ(blob2.Data(), result2); +} + +/** + * @tc.name: AppBlobToString001 + * @tc.desc: Construct a Blob and check its ToString function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobToString001, TestSize.Level0) +{ + AppBlob blob1("1234"); + std::string str = "1234"; + EXPECT_EQ(blob1.ToString(), str); +} + +/** + * @tc.name: AppBlobOperatorEqual001 + * @tc.desc: Construct a Blob and check its operator== function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobOperatorEqual001, TestSize.Level0) +{ + AppBlob blob1("1234"); + AppBlob blob2("1234"); + EXPECT_EQ(blob1 == blob2, true); + AppBlob blob3("12345"); + EXPECT_EQ(blob1 == blob3, false); +} + +/** + * @tc.name: AppBlobOperator002 + * @tc.desc: Construct a Blob and check its operator= function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobOperator002, TestSize.Level0) +{ + AppBlob blob1("1234"); + AppBlob blob2 = blob1; + EXPECT_EQ(blob1 == blob2, true); + EXPECT_EQ(blob2.ToString(), "1234"); + blob2 = blob1; + EXPECT_EQ(blob1 == blob2, true); + EXPECT_EQ(blob2.ToString(), "1234"); + blob2 = std::move(blob1); + EXPECT_EQ(blob1 == blob2, true); + EXPECT_EQ(blob2.ToString(), "1234"); +} + +/** + * @tc.name: AppBlobOperator003 + * @tc.desc: Construct a Blob and check its operator= function. + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppBlobTest, AppBlobOperator003, TestSize.Level0) +{ + AppBlob blob1("1234"); + AppBlob blob2 = std::move(blob1); + EXPECT_EQ(blob1 == blob2, false); + EXPECT_EQ(blob1.Empty(), true); + EXPECT_EQ(blob2.ToString(), "1234"); +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_conflict_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_conflict_test.cpp new file mode 100755 index 000000000..2ae569c50 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_conflict_test.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AppConflictTest" + +#include "app_distributed_kv_data_manager.h" +#include +#include +#include +#include +#include +#include +#include "app_kvstore.h" +#include "app_types.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace OHOS::AppDistributedKv; + +class AppConflictTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + static std::shared_ptr manager; + static std::unique_ptr appKvStorePtr; // declare kvstore instance. + static Status statusGetKvStore; +}; + +std::shared_ptr AppConflictTest::manager; +std::unique_ptr AppConflictTest::appKvStorePtr = nullptr; +Status AppConflictTest::statusGetKvStore = Status::ERROR; +static const auto OLD_VALUE_TYPE = AppKvStoreConflictData::ConflictValueType::OLD_VALUE; +static const auto NEW_VALUE_TYPE = AppKvStoreConflictData::ConflictValueType::NEW_VALUE; +static const int TIME_SLEEP = 100000; + +struct ConflictDataTest { + AppKvStoreConflictPolicyType type = AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL; + Key key; + Value oldValue; + Value newValue; + bool oldIsDeleted = false; + bool newIsDeleted = false; + bool oldIsNative = false; + bool newIsNative = false; + bool operator==(const ConflictDataTest &comparedConflictDataTest) const + { + if (this->type == comparedConflictDataTest.type && + this->key == comparedConflictDataTest.key && + this->oldValue == comparedConflictDataTest.oldValue && + this->newValue == comparedConflictDataTest.newValue && + this->oldIsDeleted == comparedConflictDataTest.oldIsDeleted && + this->newIsDeleted == comparedConflictDataTest.newIsDeleted && + this->oldIsNative == comparedConflictDataTest.oldIsNative && + this->newIsNative == comparedConflictDataTest.newIsNative) { + return true; + } + return false; + } +}; +static std::vector g_conflictData; + +static void ConflictCallback(const AppKvStoreConflictData &data) +{ + ZLOGD("start."); + Key key; + Value oldValue; + Value newValue; + + data.GetKey(key); + data.GetValue(OLD_VALUE_TYPE, oldValue); + data.GetValue(NEW_VALUE_TYPE, newValue); + bool oldIsDeleted = data.IsDeleted(OLD_VALUE_TYPE); + bool newIsDeleted = data.IsDeleted(NEW_VALUE_TYPE); + bool oldIsNative = data.IsNative(OLD_VALUE_TYPE); + bool newIsNative = data.IsNative(NEW_VALUE_TYPE); + g_conflictData.push_back({data.GetType(), key, oldValue, newValue, oldIsDeleted, newIsDeleted, + oldIsNative, newIsNative}); + + ZLOGD("Get key: %s", key.ToString().c_str()); + ZLOGD("Get old value: %s", oldValue.ToString().c_str()); + ZLOGD("Get oldIsDeleted: %d", oldIsDeleted); + ZLOGD("Get newIsDeleted: %d", newIsDeleted); + ZLOGD("Get oldIsNative: %d", oldIsNative); + ZLOGD("Get newIsNative: %d", newIsNative); +} + +void AppConflictTest::SetUpTestCase(void) +{ +} + +void AppConflictTest::TearDownTestCase(void) +{ +} + +void AppConflictTest::SetUp(void) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; // not supported yet. + options.persistant = true; // not supported yet. + + std::string appId = "odmf"; // define app name. + std::string storeId = "conflictdb"; // define kvstore(database) name. + std::string dataDir = "data/misc_ce/0/odmf_test"; + + manager = AppDistributedKvDataManager::GetInstance(appId, dataDir); + // [create and] open and initialize kvstore instance. + statusGetKvStore = manager->GetKvStore(options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + + g_conflictData.clear(); +} + +void AppConflictTest::TearDown(void) +{ + Status statusRet = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(statusRet, Status::SUCCESS); + statusRet = manager->DeleteKvStore("conflictdb"); + EXPECT_EQ(statusRet, Status::SUCCESS); +} + +/** + * @tc.name: AppConflict001 + * @tc.desc: set conflict resolution policy and no conflict + * @tc.type: FUNC + * @tc.require: AR000CQDUG + * @tc.author: liuyuhui + */ +HWTEST_F(AppConflictTest, AppConflict001, TestSize.Level0) +{ + ZLOGD("AppConflict001"); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, appKvStorePtr) << "appKvStorePtr is nullptr"; + + Status status = appKvStorePtr->SetConflictResolutionPolicy(AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL, + ConflictCallback); + EXPECT_EQ(Status::SUCCESS, status) << "SetConflictResolutionPolicy return wrong status"; + WriteOptions localWrite; + localWrite.local = false; + status = appKvStorePtr->Put(localWrite, "Id1", "conflict"); + EXPECT_EQ(Status::SUCCESS, status) << "Put return wrong status"; + usleep(TIME_SLEEP); + EXPECT_EQ(g_conflictData.empty(), true); +} + +/** + * @tc.name: AppConflict002 + * @tc.desc: set conflict resolution policy and exist conflict -put the same key + * @tc.type: FUNC + * @tc.require: AR000CQDUG + * @tc.author: liuyuhui + */ +HWTEST_F(AppConflictTest, AppConflict002, TestSize.Level0) +{ + ZLOGD("AppConflict002"); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, appKvStorePtr) << "appKvStorePtr is nullptr"; + + WriteOptions localWrite; + localWrite.local = false; + Status status = appKvStorePtr->Put(localWrite, "Id2", "conflict"); + EXPECT_EQ(Status::SUCCESS, status) << "Put return wrong status"; + usleep(TIME_SLEEP); + + status = appKvStorePtr->SetConflictResolutionPolicy(AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL, + ConflictCallback); + EXPECT_EQ(Status::SUCCESS, status) << "SetConflictResolutionPolicy return wrong status"; + status = appKvStorePtr->Put(localWrite, "Id2", "conflict_modify"); + EXPECT_EQ(Status::SUCCESS, status) << "Put return wrong status"; + usleep(TIME_SLEEP); + EXPECT_EQ(g_conflictData.empty(), false); + ConflictDataTest expectData = {AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL, + "Id2", "conflict", "conflict_modify", false, false, true, true}; + EXPECT_EQ(g_conflictData.front() == expectData, true); +} + +/** + * @tc.name: AppConflict003 + * @tc.desc: set conflict resolution policy and exist conflict -put and delete the same key + * @tc.type: FUNC + * @tc.require: AR000CQDUG + * @tc.author: liuyuhui + */ +HWTEST_F(AppConflictTest, AppConflict003, TestSize.Level0) +{ + ZLOGD("app_conflict_003 start"); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, appKvStorePtr) << "appKvStorePtr is nullptr"; + + WriteOptions localWrite; + localWrite.local = false; + Status status = appKvStorePtr->Put(localWrite, "Id3", "conflict"); + EXPECT_EQ(Status::SUCCESS, status) << "Put return wrong status"; + usleep(TIME_SLEEP); + + status = appKvStorePtr->SetConflictResolutionPolicy(AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL, + ConflictCallback); + EXPECT_EQ(Status::SUCCESS, status) << "SetConflictResolutionPolicy return wrong status"; + status = appKvStorePtr->Delete(localWrite, "Id3"); + EXPECT_EQ(Status::SUCCESS, status) << "Delete return wrong status"; + usleep(TIME_SLEEP); + EXPECT_EQ(g_conflictData.empty(), false); + Value newValue; + ConflictDataTest expectData = {AppKvStoreConflictPolicyType::CONFLICT_NATIVE_ALL, + "Id3", "conflict", newValue, false, true, true, true}; + EXPECT_EQ(g_conflictData.front() == expectData, true); + ZLOGD("app_conflict_003 end"); +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_data_manager_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_data_manager_test.cpp new file mode 100755 index 000000000..60ae5c5e7 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_data_manager_test.cpp @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_distributed_kv_data_manager.h" +#include +#include +#include +#include "app_kvstore.h" +#include "app_types.h" +#include "app_kvstore_corruption_observer.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "AppDistributedKvDataManagerTest" + +using namespace testing::ext; +using namespace OHOS::AppDistributedKv; + +class AppDistributedKvDataManagerTest : public testing::Test { +public: + static std::shared_ptr manager; + static Options create; + static Options noCreate; + + static std::string appId; + static std::string storeId64; + static std::string storeId65; + static std::string storeIdEmpty; + + static void SetUpTestCase(void); + static void TearDownTestCase(void); + + static void RemoveAllStore(AppDistributedKvDataManager &manager); + + void SetUp(); + void TearDown(); + AppDistributedKvDataManagerTest(); +}; + +std::shared_ptr AppDistributedKvDataManagerTest::manager; +Options AppDistributedKvDataManagerTest::create; +Options AppDistributedKvDataManagerTest::noCreate; + +std::string AppDistributedKvDataManagerTest::appId; +std::string AppDistributedKvDataManagerTest::storeId64; +std::string AppDistributedKvDataManagerTest::storeId65; +std::string AppDistributedKvDataManagerTest::storeIdEmpty; + +void AppDistributedKvDataManagerTest::SetUpTestCase(void) +{ + create.createIfMissing = true; + create.encrypt = false; + create.persistant = true; + + noCreate.createIfMissing = false; + noCreate.encrypt = false; + noCreate.persistant = true; + + appId = "com.ohos.nb.service"; + std::string dataDir = "data/misc_ce/0/com.ohos.nb.service"; + storeId64 = "a000000000b000000000c000000000d000000000e000000000f000000000g000"; + storeId65 = "a000000000b000000000c000000000d000000000e000000000f000000000g0000" + "a000000000b000000000c000000000d000000000e000000000f000000000g000"; + storeIdEmpty = ""; + std::string userId = "ohosAnonymousUid"; + + manager = AppDistributedKvDataManager::GetInstance(appId, dataDir, userId); +} + +void AppDistributedKvDataManagerTest::TearDownTestCase(void) +{ + manager->DeleteKvStore(storeId64); +} + +void AppDistributedKvDataManagerTest::SetUp(void) +{ + manager->DeleteKvStore(storeId64); +} + +AppDistributedKvDataManagerTest::AppDistributedKvDataManagerTest(void) +{} + +void AppDistributedKvDataManagerTest::TearDown(void) +{ + manager->DeleteKvStore(storeId64); +} + +/** + * @tc.name: AppManagerGetKvstore001 + * @tc.desc: Get an exist KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore001, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + std::unique_ptr existKvStorePtr; + status = manager->GetKvStore(noCreate, storeId64, [&](std::unique_ptr kvStore) { + existKvStorePtr = std::move(kvStore); + }); + EXPECT_NE(existKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->CloseKvStore(std::move(notExistKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->CloseKvStore(std::move(existKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppManagerGetKvstore002 + * @tc.desc: Create and get a new KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore002, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->CloseKvStore(std::move(notExistKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppManagerGetKvstore003 + * @tc.desc: Get a non-existing KvStore, and the callback function should receive STORE_NOT_FOUND and + * get a nullptr. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore003, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(noCreate, storeId64, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::STORE_NOT_FOUND); +} + +/** + * @tc.name: AppManagerGetKvstore004 + * @tc.desc: Create a KvStore with an empty storeId, and the callback function should receive + * INVALID_ARGUMENT and got a nullptr. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore004, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(create, storeIdEmpty, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerGetKvstore005 + * @tc.desc: Get a KvStore with an empty storeId, the callback function should receive INVALID_ARGUMENT + * and got a nullptr. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore005, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(noCreate, storeIdEmpty, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerGetKvstore006 + * @tc.desc: Create a KvStore with 65-byte storeId, and the callback function should receive + * INVALID_ARGUMENT and got a nullptr. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore006, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId65, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerGetKvstore007 + * @tc.desc: Get a KvStore with 65-byte storeId, and the callback function should receive + * INVALID_ARGUMENT and got a nullptr. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerGetKvstore007, TestSize.Level0) +{ + std::unique_ptr notExistKvStorePtr; + Status status; + status = manager->GetKvStore(noCreate, storeId65, [&](std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerCloseKvstore001 + * @tc.desc: Close an opened KVStore, and the callback should return SUCCESS. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerCloseKvstore001, TestSize.Level0) +{ + std::unique_ptr kvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + }); + ASSERT_NE(kvStorePtr, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + + Status stat = manager->CloseKvStore(std::move(kvStorePtr)); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** + * @tc.name: AppManagerCloseKvstore002 + * @tc.desc: Close a closed KvStore, and the callback should return INVALID_ARGUMENT. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerCloseKvstore002, TestSize.Level0) +{ + std::unique_ptr kvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + }); + ASSERT_NE(kvStorePtr, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + + manager->CloseKvStore(std::move(kvStorePtr)); + Status stat = manager->CloseKvStore(std::move(kvStorePtr)); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerCloseKvstore003 + * @tc.desc: Close a KvStore with empty storeId, and the callback should return INVALID_ARGUMENT. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerCloseKvstore003, TestSize.Level0) +{ + std::unique_ptr kvStorePtr = nullptr; + Status stat = manager->CloseKvStore(nullptr); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerDeleteKvStore001 + * @tc.desc: Delete a closed KvStore, and the callback should return SUCCESS. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerDeleteKvStore001, TestSize.Level0) +{ + std::unique_ptr kvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + }); + ASSERT_NE(kvStorePtr, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + + Status stat = manager->CloseKvStore(std::move(kvStorePtr)); + ASSERT_EQ(stat, Status::SUCCESS); + + stat = manager->DeleteKvStore(storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** + * @tc.name: AppManagerDeleteKvStore002 + * @tc.desc: Delete a opened KvStore, and the callback should return ILLEGAL_STATE. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerDeleteKvStore002, TestSize.Level0) +{ + std::unique_ptr kvStorePtr; + Status status; + status = manager->GetKvStore(create, storeId64, [&](std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + }); + ASSERT_NE(kvStorePtr, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + + // first close it if opened, and then delete it. + status = manager->DeleteKvStore(storeId64); + EXPECT_EQ(status, Status::ERROR); + manager->CloseKvStore(std::move(kvStorePtr)); +} + +/** + * @tc.name: AppManagerDeleteKvStore003 + * @tc.desc: Delete a non-existing KvStore, and the callback should return ERROR. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerDeleteKvStore003, TestSize.Level0) +{ + Status stat = manager->DeleteKvStore(storeId64); + EXPECT_EQ(stat, Status::ERROR); +} + +/** + * @tc.name: AppManagerDeleteKvStore004 + * @tc.desc: Delete a KvStore with an empty storeId, and the callback should return INVALID_ARGUMENT. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerDeleteKvStore004, TestSize.Level0) +{ + Status stat = manager->DeleteKvStore(storeIdEmpty); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: AppManagerDeleteKvStore005 + * @tc.desc: Delete a KvStore with a 65-byte storeId (which exceeds storeId length limit), and the calback should + * return INVALID_ARGUMENT. + * @tc.type: FUNC + * @tc.require: AR000CCPOJ + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvDataManagerTest, AppManagerDeleteKvStore005, TestSize.Level0) +{ + Status stat = manager->DeleteKvStore(storeId65); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** + * @tc.name: GetKvStoreDiskSize001 + * @tc.desc: Get the kvStore disk size. + * @tc.type: FUNC + * @tc.require: AR000CQDTD + * @tc.author: hongbo + */ +HWTEST_F(AppDistributedKvDataManagerTest, GetKvStoreDiskSize001, TestSize.Level0) +{ + uint64_t size; + Status stat = manager->GetKvStoreDiskSize(storeId65, size); + if (stat == Status::SUCCESS) { + uint64_t expect = 0; + EXPECT_GE(size, expect); + } +} + +class CorruptionObserverImpl : public AppKvStoreCorruptionObserver { +public: + ~CorruptionObserverImpl() {} + void OnCorruption(const std::string &appId, const std::string &userId, const std::string &storeId) override; +}; + +void CorruptionObserverImpl::OnCorruption(const std::string &appId, const std::string &userId, + const std::string &storeId) {} +/** + * @tc.name: RegisterKvStoreCorruptionObserver + * @tc.desc: Register the database corruption observer. + * @tc.type: FUNC + * @tc.require: AR000D487D + * @tc.author: hongbo + */ +HWTEST_F(AppDistributedKvDataManagerTest, RegisterKvStoreCorruptionObserver001, TestSize.Level0) +{ + auto observer = std::make_shared(); + Status stat = manager->RegisterKvStoreCorruptionObserver(observer); + EXPECT_EQ(stat, Status::SUCCESS); +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_store_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_store_test.cpp new file mode 100755 index 000000000..2055dd256 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/app_distributed_kv_store_test.cpp @@ -0,0 +1,1698 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_distributed_kv_data_manager.h" +#include +#include +#include +#include +#include +#include +#include +#include "app_kvstore.h" +#include "app_types.h" +#include "log_print.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "AppDistributedKvDataManagerTest" + +using namespace testing::ext; +using namespace OHOS::AppDistributedKv; + +class AppDistributedKvStoreTest : public testing::Test { +public: + static std::shared_ptr manager; + static WriteOptions localWrite, syncWrite; + static ReadOptions localRead, syncRead; + static Options options; + static std::string appId; + static std::string storeId; + static std::string dataDir; + static std::string userId; + static SubscribeType subscribeType; + + static void SetUpTestCase(void); + static void TearDownTestCase(void); + + void SetUp(); + void TearDown(); + AppDistributedKvStoreTest(); +}; + +std::shared_ptr AppDistributedKvStoreTest::manager; + +ReadOptions AppDistributedKvStoreTest::localRead; +ReadOptions AppDistributedKvStoreTest::syncRead; +WriteOptions AppDistributedKvStoreTest::localWrite; +WriteOptions AppDistributedKvStoreTest::syncWrite; +Options AppDistributedKvStoreTest::options; + +SubscribeType AppDistributedKvStoreTest::subscribeType = SubscribeType::DEFAULT; +std::string AppDistributedKvStoreTest::dataDir = "data/misc_ce/0/appKvStoreTest"; +std::string AppDistributedKvStoreTest::appId = "appKvStoreTest"; +std::string AppDistributedKvStoreTest::storeId = "appKvStore0"; +std::string AppDistributedKvStoreTest::userId = "domainUser0"; + +void AppDistributedKvStoreTest::SetUpTestCase(void) +{ + manager = AppDistributedKvDataManager::GetInstance(appId, dataDir, userId); + localRead.local = true; + syncRead.local = false; + localWrite.local = true; + syncWrite.local = false; + options.createIfMissing = true; + options.encrypt = false; + options.persistant = true; +} + +void AppDistributedKvStoreTest::TearDownTestCase(void) +{ +} + +void AppDistributedKvStoreTest::SetUp(void) +{ +} + +AppDistributedKvStoreTest::AppDistributedKvStoreTest(void) +{ +} + +void AppDistributedKvStoreTest::TearDown(void) +{ +} + +class ObserverImpl : public AppKvStoreObserver { +public: + std::vector insertEntries; + std::vector updateEntries; + std::vector deleteEntries; + bool isClear; + ObserverImpl() + { + insertEntries = {}; + updateEntries = {}; + deleteEntries = {}; + isClear = false; + callCount_ = 0; + } + ~ObserverImpl() = default; + + int CallCount() + { + return callCount_; + } + + void OnChange(const AppChangeNotification &appChangeNotification) override + { + const std::list insert = appChangeNotification.GetInsertEntries(); + insertEntries.clear(); + for (const auto &entry : insert) { + insertEntries.push_back(entry); + } + + const std::list update = appChangeNotification.GetUpdateEntries(); + updateEntries.clear(); + for (const auto &entry : update) { + updateEntries.push_back(entry); + } + + const std::list del = appChangeNotification.GetDeleteEntries(); + deleteEntries.clear(); + for (const auto &entry : del) { + deleteEntries.push_back(entry); + } + + isClear = appChangeNotification.IsClear(); + callCount_ += 1; + + ZLOGI("AppChangeNotification OnChange GetDeviceId"); + appChangeNotification.GetDeviceId(); + } + + void Clear() + { + insertEntries.clear(); + updateEntries.clear(); + deleteEntries.clear(); + isClear = false; + callCount_ = 0; + } +private: + int callCount_; +}; + +/** + * @tc.name: AppKvstorePut001 + * @tc.desc: put int data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut001, TestSize.Level0) +{ + // cover app_kvstore_put_001, app_kvstore_put_local_001, + // app_kvstore_get_001, app_kvstore_get_local_001 + // app_kvstore_independent_001, app_kvstore_independent_002 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_int"), Value(TransferTypeToByteArray(-383468))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_int"), Value(TransferTypeToByteArray(-383469))); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_int"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), -383468); + status = appKvStorePtr->Get(syncRead, Key("math_score_int"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), -383469); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut002 + * @tc.desc: put float data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut002, TestSize.Level0) +{ + // cover app_kvstore_put_002, app_kvstore_put_local_002 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_float"), Value(TransferTypeToByteArray(3.14f))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_float"), Value(TransferTypeToByteArray(3.1416f))); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_float"), ret); + EXPECT_EQ(status, Status::SUCCESS); + float delta = TransferByteArrayToType(ret.Data()) - 3.14f; + EXPECT_LE(std::abs(delta), 0.00001); + status = appKvStorePtr->Get(syncRead, Key("math_score_float"), ret); + EXPECT_EQ(status, Status::SUCCESS); + delta = TransferByteArrayToType(ret.Data()) - 3.1416f; + EXPECT_LE(std::abs(delta), 0.00001); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut003 + * @tc.desc: put double data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut003, TestSize.Level0) +{ + // cover app_kvstore_put_003, app_kvstore_put_local_003 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_double"), Value(TransferTypeToByteArray(28.785f))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_double"), Value(TransferTypeToByteArray(28.790f))); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_double"), ret); + EXPECT_EQ(status, Status::SUCCESS); + double delta = TransferByteArrayToType(ret.Data()) - 28.785f; + EXPECT_LE(std::abs(delta), 0.00001); + status = appKvStorePtr->Get(syncRead, Key("math_score_double"), ret); + EXPECT_EQ(status, Status::SUCCESS); + delta = TransferByteArrayToType(ret.Data()) - 28.790f; + EXPECT_LE(std::abs(delta), 0.00001); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut004 + * @tc.desc: put size_t data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut004, TestSize.Level0) +{ + // cover app_kvstore_put_004, app_kvstore_put_local_004 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_size_t"), Value(TransferTypeToByteArray(28))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_size_t"), Value(TransferTypeToByteArray(29))); + EXPECT_EQ(status, Status::SUCCESS); + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_size_t"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), 28u); + status = appKvStorePtr->Get(syncRead, Key("math_score_size_t"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), 29u); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut005 + * @tc.desc: put int64_t data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut005, TestSize.Level0) +{ + // cover app_kvstore_put_005, app_kvstore_put_local_005 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = + appKvStorePtr->Put(localWrite, Key("math_score_int64_t"), Value(TransferTypeToByteArray(12345678))); + EXPECT_EQ(status, Status::SUCCESS); + status = + appKvStorePtr->Put(syncWrite, Key("math_score_int64_t"), Value(TransferTypeToByteArray(123456789))); + EXPECT_EQ(status, Status::SUCCESS); + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_int64_t"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), 12345678u); + status = appKvStorePtr->Get(syncRead, Key("math_score_int64_t"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(TransferByteArrayToType(ret.Data()), 123456789u); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut006 + * @tc.desc: put string data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut006, TestSize.Level0) +{ + // cover app_kvstore_put_006, app_kvstore_put_local_006 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("student_name_zhangsan"), + Value("{\"class\":20, \"age\":18, \"gradle\":\"good\"}")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhangsan"), + Value("{\"class\":20, \"age\":19, \"gradle\":\"good\"}")); + EXPECT_EQ(status, Status::SUCCESS); + Value ret; + status = appKvStorePtr->Get(localRead, Key("student_name_zhangsan"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("{\"class\":20, \"age\":18, \"gradle\":\"good\"}")); + status = appKvStorePtr->Get(syncRead, Key("student_name_zhangsan"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("{\"class\":20, \"age\":19, \"gradle\":\"good\"}")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut007 + * @tc.desc: put byte array data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut007, TestSize.Level0) +{ + // cover app_kvstore_put_007, app_kvstore_put_local_007 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + std::vector val = {0, 1, 2, 0, 0, 5, 6, 0}; + status = appKvStorePtr->Put(localWrite, Key("teacher_name_wanger"), + Value(val)); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("teacher_name_wanger"), + Value(val)); + EXPECT_EQ(status, Status::SUCCESS); + Value ret; + status = appKvStorePtr->Get(localRead, Key("teacher_name_wanger"), ret); + EXPECT_EQ(status, Status::SUCCESS); + ASSERT_EQ(ret.Size(), val.size()); + for(unsigned long i = 0; i < ret.Size(); i++) { + EXPECT_EQ(ret.Data()[i], val[i]); + } + status = appKvStorePtr->Get(syncRead, Key("teacher_name_wanger"), ret); + EXPECT_EQ(status, Status::SUCCESS); + ASSERT_EQ(ret.Size(), val.size()); + for(unsigned long i = 0; i < ret.Size(); i++) { + EXPECT_EQ(ret.Data()[i], val[i]); + } + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut008 + * @tc.desc: Put data including localWrite and syncWrite to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut008, TestSize.Level0) +{ + // cover app_kvstore_put_008, app_kvstore_put_local_008 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("teacher_name_wanger"), Value("class:20, age:50")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("teacher_name_wanger"), Value("class:20, age:51")); + EXPECT_EQ(status, Status::SUCCESS); + Value ret; + status = appKvStorePtr->Get(localRead, Key("teacher_name_wanger"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("class:20, age:50")); + status = appKvStorePtr->Get(syncRead, Key("teacher_name_wanger"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("class:20, age:51")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut009 + * @tc.desc: Update data including localWrite and syncWrite in KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut009, TestSize.Level0) +{ + // cover app_kvstore_put_009, app_kvstore_put_local_009 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("student_name_lisi"), Value("age:18")); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("student_name_lisi"), Value("age:20")); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(syncWrite, Key("student_name_lisi"), Value("age:19")); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(syncWrite, Key("student_name_lisi"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("student_name_lisi"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("age:20")); + status = appKvStorePtr->Get(syncRead, Key("student_name_lisi"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.ToString(), std::string("age:21")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut010 + * @tc.desc: put invalid key data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut010, TestSize.Level0) +{ + // cover app_kvstore_put_010, app_kvstore_put_local_010 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key(""), Value("age:18")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = appKvStorePtr->Put(syncWrite, Key(""), Value("age:19")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut011 + * @tc.desc: Put empty value data to KvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut011, TestSize.Level0) +{ + // cover app_kvstore_put_011, app_kvstore_put_local_011 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("student_name_lisi"), Value("")); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(syncWrite, Key("student_name_lisi"), Value("")); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +std::string Generate1025KeyLen() +{ + // Generate key and the length is more than 1024; + std::string str("prefix"); + for (int i = 0; i < 1024; i++) { + str += "a"; + } + return str; +} + +/** + * @tc.name: AppKvstorePut012 + * @tc.desc: Put key (greater than or equal to 256 bytes) data to KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut012, TestSize.Level0) +{ + // cover app_kvstore_put_012, app_kvstore_put_local_012 + std::unique_ptr appKvStorePtr; + Status status; + std::string str16 = "0123456789abcdef"; + std::string str256 = ""; + for (int i = 0; i < 16; i++) { + str256 = str256 + str16; + } + std::string str257 = str256 + "g"; + ASSERT_EQ(256UL, str256.size()); + ASSERT_EQ(257UL, str257.size()); + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key(str256), Value("class:2, age:50")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key(Generate1025KeyLen()), Value("class:2, age:50")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = appKvStorePtr->Put(syncWrite, Key(str256), Value("class:2, age:51")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key(Generate1025KeyLen()), Value("class:2, age:51")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut013 + * @tc.desc: put invalid key data to KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, app_kvstore_put_013, TestSize.Level0) +{ + // cover app_kvstore_put_013, app_kvstore_put_local_013 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key(" "), Value("class:2, age:50")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = appKvStorePtr->Put(syncWrite, Key(" "), Value("class:2, age:51")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstorePut014 + * @tc.desc: Put value (greater than or equal to 1 M) data to KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstorePut014, TestSize.Level0) +{ + // cover app_kvstore_put_014, app_kvstore_put_local_014 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + constexpr int VALUE_MAX_SIZE = 4 * 1024 * 1024; + std::vector valueData(VALUE_MAX_SIZE); + for(int i = 0; i < VALUE_MAX_SIZE; i++) { + valueData[i] = 'a'; + } + + status = appKvStorePtr->Put(localWrite, Key("student_name_lisi"), Value(valueData)); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_lisa"), Value(valueData)); + EXPECT_EQ(status, Status::SUCCESS); + + valueData.push_back('a'); + status = appKvStorePtr->Put(localWrite, Key("student_name_lisi"), Value(valueData)); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + status = appKvStorePtr->Put(syncWrite, Key("student_name_lisa"), Value(valueData)); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreDelete001 + * @tc.desc: Delete data from KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreDelete001, TestSize.Level0) +{ + // cover app_kvstore_delete_001, app_kvstore_delete_local_001 + // app_kvstore_get_002, app_kvstore_get_local_002 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_int"), Value(TransferTypeToByteArray(-383468))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(localWrite, Key("math_score_int")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_int_another"), Value(TransferTypeToByteArray(-383468))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("math_score_int_another")); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_int"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + status = appKvStorePtr->Get(localRead, Key("math_score_int_another"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreDelete002 + * @tc.desc: Delete a key which does not exist. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreDelete002, TestSize.Level0) +{ + // cover app_kvstore_delete_002, app_kvstore_delete_local_002 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("math_score_int"), Value(TransferTypeToByteArray(-383468))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(localWrite, Key("math_score_int")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(localWrite, Key("math_score_int")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("math_score_int_another"), Value(TransferTypeToByteArray(-383468))); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("math_score_int_another")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("math_score_int_another")); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("math_score_int"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + status = appKvStorePtr->Get(localRead, Key("math_score_int_another"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreDelete003 + * @tc.desc: Delete a key which is invalid. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreDelete003, TestSize.Level0) +{ + // cover app_kvstore_delete_003, app_kvstore_delete_local_003 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Delete(localWrite, Key("")); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGet001 + * @tc.desc: Get data from KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGet001, TestSize.Level0) +{ + // cover app_kvstore_get_003, app_kvstore_get_local_003 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + Value ret; + status = appKvStorePtr->Get(localRead, Key("student_name_lisi"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + status = appKvStorePtr->Get(syncRead, Key("student_name_lisi"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGetEntries001 + * @tc.desc: Get entries with prefix from KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGetEntries001, TestSize.Level0) +{ + // cover app_kvstore_getentries_001, app_kvstore_getentries_local_001 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + std::vector ret; + status = appKvStorePtr->Put(localWrite, Key("student_name_mali"), Value("age:20")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("student_name_caixu"), Value("age:19")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("student_name_liuyue"), Value("age:23")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_maliang"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhuangzhou"), Value("age:22")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_liuyuxi"), Value("age:24")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("teacher_name_libai"), Value("age:25")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->GetEntries(Key("student_name_"), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.size(), 3UL); + + std::map result; + for (Entry entry : ret) { + result.insert(std::pair(entry.key.ToString(), entry.value.ToString())); + } + EXPECT_EQ(result["student_name_maliang"], std::string("age:21")); + EXPECT_EQ(result["student_name_zhuangzhou"], std::string("age:22")); + EXPECT_EQ(result["student_name_liuyuxi"], std::string("age:24")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGetEntries002 + * @tc.desc: Get entries with non-existing prefix from KvStore. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGetEntries002, TestSize.Level0) +{ + // cover app_kvstore_getentries_002, app_kvstore_getentries_local_002 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + std::vector ret; + status = appKvStorePtr->Put(syncWrite, Key("student_name_maliang"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhuangzhou"), Value("age:22")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_liuyuxi"), Value("age:24")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->GetEntries(Key("teacher_name_"), ret); + EXPECT_EQ(status, Status::KEY_NOT_FOUND); + EXPECT_EQ(ret.size(), 0UL); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGetEntries003 + * @tc.desc: Get entries with prefix (empty string) from KvStore, and all data should be returned. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGetEntries003, TestSize.Level0) +{ + // cover app_kvstore_getentries_003, app_kvstore_getentries_local_003 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + std::vector ret; + status = appKvStorePtr->Put(localWrite, Key("student_name_mali"), Value("age:20")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_maliang"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhuangzhou"), Value("age:22")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_liuyuxi"), Value("age:24")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->GetEntries(Key(""), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.size(), 3UL); + + std::map result; + for (Entry entry : ret) { + result.insert(std::pair(entry.key.ToString(), entry.value.ToString())); + } + EXPECT_EQ(result["student_name_maliang"], std::string("age:21")); + EXPECT_EQ(result["student_name_zhuangzhou"], std::string("age:22")); + EXPECT_EQ(result["student_name_liuyuxi"], std::string("age:24")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGetEntries004 + * @tc.desc: Get entries with prefix (empty string) from KvStore, and all data should be returned. + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGetEntries004, TestSize.Level0) +{ + // cover app_kvstore_getentries_004, app_kvstore_getentries_local_004 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + std::vector ret; + status = appKvStorePtr->Put(localWrite, Key("student_name_mali"), Value("age:20")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_maliang"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhuangzhou"), Value("age:22")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_liuyuxi"), Value("age:24")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->GetEntries(Key(" "), ret); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(ret.size(), 3UL); + + std::map result; + for (Entry entry : ret) { + result.insert(std::pair(entry.key.ToString(), entry.value.ToString())); + } + EXPECT_EQ(result["student_name_maliang"], std::string("age:21")); + EXPECT_EQ(result["student_name_zhuangzhou"], std::string("age:22")); + EXPECT_EQ(result["student_name_liuyuxi"], std::string("age:24")); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreGetEntries005 + * @tc.desc: GetEntries + * @tc.type: FUNC + * @tc.require: AR000CCPOL AR000CQS36 + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreGetEntries005, TestSize.Level0) +{ + // cover app_kvstore_getentries_005, app_kvstore_getentries_local_005 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, "student", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + std::string str16 = "0123456789abcdef"; + std::string str240 = ""; + for (int i = 0; i < 15; i++) { + str240 = str240 + str16; + } + std::vector ret; + status = appKvStorePtr->Put(localWrite, Key("student_name_mali"), Value("age:20")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_maliang"), Value("age:21")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_zhuangzhou"), Value("age:22")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("student_name_liuyuxi"), Value("age:24")); + EXPECT_EQ(status, Status::SUCCESS); + ASSERT_EQ((std::string("student_name_lisi") + str240).size(), 257UL); // key have max size 256 + status = appKvStorePtr->GetEntries(Key(std::string("student_name_lisi") + Generate1025KeyLen()), ret); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("student"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore001 + * @tc.desc: SubscribeKvStore and revieve callback + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore001, TestSize.Level0) +{ + // cover app_kvstore_subscribekvstore_001, app_kvstore_subscribekvstore_003, + // app_kvstore_subscribekvstore_local_001, app_kvstore_subscribekvstore_local_003 + + // this testcase fails with a small probable. please leave this log print alone. + ZLOGI("testcase app_kvstore_subscribekvstore_001"); + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerLocal = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerSync = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + + observerLocal->Clear(); + observerSync->Clear(); + status = appKvStorePtr->Put(localWrite, Key("key_s1"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->insertEntries.size(), 1UL); + EXPECT_EQ(observerLocal->deleteEntries.size(), 0UL); + EXPECT_EQ(observerSync->insertEntries.size(), 0UL) << "contain " << observerSync->insertEntries[0].key.ToString(); + EXPECT_EQ(observerSync->deleteEntries.size(), 0UL) << "contain " << observerSync->deleteEntries[0].key.ToString(); + std::cout << observerLocal->CallCount() << observerSync->CallCount() << std::endl; + + observerLocal->Clear(); + observerSync->Clear(); + status = appKvStorePtr->Put(syncWrite, Key("key_s1"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key_s1")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->insertEntries.size(), 0UL); + EXPECT_EQ(observerLocal->deleteEntries.size(), 0UL); + EXPECT_EQ(observerSync->insertEntries.size(), 0UL) << "contain " << observerSync->insertEntries[0].key.ToString(); + EXPECT_EQ(observerSync->deleteEntries.size(), 1UL); + std::cout << observerLocal->CallCount() << observerSync->CallCount() << std::endl; + + EXPECT_EQ(observerLocal->isClear, false); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete observerLocal; + delete observerSync; + observerLocal = nullptr; + observerSync = nullptr; +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore002 + * @tc.desc: SubscribeKvStore observer is nullptr + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore002, TestSize.Level0) +{ + // cover app_kvstore_subscribekvstore_002, app_kvstore_subscribekvstore_local_002 + std::unique_ptr appKvStorePtr; + Status status; + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, nullptr); + EXPECT_EQ(status, Status::INVALID_ARGUMENT); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore003 + * @tc.desc: the same observer SubscribeKvStore many times + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore003, TestSize.Level2) +{ + // cover app_kvstore_subscribekvstore_004, app_kvstore_subscribekvstore_local_004 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + ObserverImpl *observerLocal = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::STORE_ALREADY_SUBSCRIBE); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::STORE_ALREADY_SUBSCRIBE); + ObserverImpl *observerSync = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::STORE_ALREADY_SUBSCRIBE); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::STORE_ALREADY_SUBSCRIBE); + + status = appKvStorePtr->Put(localWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->CallCount(), 1); + EXPECT_EQ(observerSync->CallCount(), 0); + + status = appKvStorePtr->Put(syncWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->CallCount(), 1); + EXPECT_EQ(observerSync->CallCount(), 2); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete observerLocal; + delete observerSync; + observerLocal = nullptr; + observerSync = nullptr; +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore004 + * @tc.desc: the different observer SubscribeKvStore many times + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore004, TestSize.Level2) +{ + // cover app_kvstore_subscribekvstore_004, app_kvstore_subscribekvstore_local_004 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + ObserverImpl *observerLocal1 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal1); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerLocal2 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal2); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerLocal3 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal3); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerSync1 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync1); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerSync2 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync2); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerSync3 = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync3); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal1->CallCount(), 1); + EXPECT_EQ(observerLocal2->CallCount(), 1); + EXPECT_EQ(observerLocal3->CallCount(), 1); + EXPECT_EQ(observerSync1->CallCount(), 0); + EXPECT_EQ(observerSync2->CallCount(), 0); + EXPECT_EQ(observerSync3->CallCount(), 0); + + status = appKvStorePtr->Put(syncWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal1->CallCount(), 1); + EXPECT_EQ(observerLocal2->CallCount(), 1); + EXPECT_EQ(observerLocal3->CallCount(), 1); + EXPECT_EQ(observerSync1->CallCount(), 2); + EXPECT_EQ(observerSync2->CallCount(), 2); + EXPECT_EQ(observerSync3->CallCount(), 2); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete observerLocal1; + delete observerLocal2; + delete observerLocal3; + delete observerSync1; + delete observerSync2; + delete observerSync3; + observerLocal1 = nullptr; + observerLocal2 = nullptr; + observerLocal3 = nullptr; + observerSync1 = nullptr; + observerSync2 = nullptr; + observerSync3 = nullptr; +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore005 + * @tc.desc: SubscribeKvStore and then unSubscribeKvStore + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore005, TestSize.Level2) +{ + // cover app_kvstore_subscribekvstore_004, app_kvstore_subscribekvstore_local_004 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + ObserverImpl *observerLocal = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerSync = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(localWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->CallCount(), 1); + EXPECT_EQ(observerSync->CallCount(), 0); + status = appKvStorePtr->Put(syncWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->CallCount(), 1); + EXPECT_EQ(observerSync->CallCount(), 1); + + status = appKvStorePtr->UnSubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->UnSubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + observerLocal->Clear(); + observerSync->Clear(); + + + status = appKvStorePtr->Put(syncWrite, Key("key"), Value("value")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(observerLocal->CallCount(), 0); + EXPECT_EQ(observerSync->CallCount(), 0); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete observerLocal; + delete observerSync; + observerLocal = nullptr; + observerSync = nullptr; +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore006 + * @tc.desc: SubscribeKvStore and then unSubscribeKvStore, verifying callback condition + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore006, TestSize.Level2) +{ + // cover app_kvstore_subscribekvstore_007~011, app_kvstore_subscribekvstore_local_007~011 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->Put(syncWrite, Key("key"), Value("syncValue")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("key"), Value("localValue")); + EXPECT_EQ(status, Status::SUCCESS); + + ObserverImpl *localObserver = new ObserverImpl(); + ObserverImpl *syncObserver = new ObserverImpl(); + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, localObserver); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(localObserver->CallCount(), 0); + EXPECT_EQ(syncObserver->CallCount(), 0); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, syncObserver); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(localObserver->CallCount(), 0); + EXPECT_EQ(syncObserver->CallCount(), 0); + + status = appKvStorePtr->Put(syncWrite, Key("key1"), Value("syncValue1_")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("key1"), Value("syncValue1")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(syncWrite, Key("key2"), Value("syncValue2")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(localObserver->CallCount(), 0); + EXPECT_EQ(syncObserver->insertEntries.size(), 1UL); + EXPECT_EQ(syncObserver->insertEntries[0].key.ToString(), std::string("key2")); + EXPECT_EQ(syncObserver->insertEntries[0].value.ToString(), std::string("syncValue2")); + EXPECT_EQ(syncObserver->deleteEntries.size(), 0UL); + EXPECT_EQ(syncObserver->CallCount(), 3); + + status = appKvStorePtr->Delete(syncWrite, Key("key1")); + sleep(1); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(localObserver->CallCount(), 0); + EXPECT_EQ(syncObserver->insertEntries.size(), 0UL); + EXPECT_EQ(syncObserver->deleteEntries.size(), 1UL); + EXPECT_EQ(syncObserver->deleteEntries[0].key.ToString(), std::string("key1")); + EXPECT_EQ(syncObserver->deleteEntries[0].value.ToString(), std::string("syncValue1")); + EXPECT_EQ(syncObserver->CallCount(), 4); + + status = appKvStorePtr->Put(localWrite, Key("key1"), Value("localvalue1_")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("key1"), Value("localvalue1")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("key2"), Value("localvalue2")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(syncObserver->CallCount(), 4); + EXPECT_EQ(localObserver->insertEntries.size(), 1UL); + EXPECT_EQ(localObserver->insertEntries[0].key.ToString(), std::string("key2")); + EXPECT_EQ(localObserver->insertEntries[0].value.ToString(), std::string("localvalue2")); + EXPECT_EQ(localObserver->deleteEntries.size(), 0UL); + EXPECT_EQ(localObserver->CallCount(), 3); + + status = appKvStorePtr->Delete(localWrite, Key("key1")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(syncObserver->CallCount(), 4); + EXPECT_EQ(localObserver->insertEntries.size(), 0UL); + EXPECT_EQ(localObserver->deleteEntries.size(), 1UL); + EXPECT_EQ(localObserver->deleteEntries[0].key.ToString(), std::string("key1")); + EXPECT_EQ(localObserver->deleteEntries[0].value.ToString(), std::string("localvalue1")); + EXPECT_EQ(localObserver->CallCount(), 4); + + status = appKvStorePtr->Delete(localWrite, Key("key10")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key10")); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(syncObserver->CallCount(), 4); + EXPECT_EQ(localObserver->CallCount(), 4); + + status = appKvStorePtr->UnSubscribeKvStore(syncRead, subscribeType, syncObserver); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->UnSubscribeKvStore(localRead, subscribeType, localObserver); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(syncObserver->CallCount(), 4); + EXPECT_EQ(localObserver->CallCount(), 4); + + status = appKvStorePtr->Put(syncWrite, Key("key1"), Value("value1")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Put(localWrite, Key("key1"), Value("value1")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(syncWrite, Key("key2")); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->Delete(localWrite, Key("key2")); + EXPECT_EQ(status, Status::SUCCESS); + sleep(1); + EXPECT_EQ(syncObserver->CallCount(), 4); + EXPECT_EQ(localObserver->CallCount(), 4); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete localObserver; + localObserver = nullptr; + delete syncObserver; + syncObserver = nullptr; +} + +/** + * @tc.name: AppKvstoreSubscribeKvStore007 + * @tc.desc: unSubscribeKvStore many times + * @tc.type: FUNC + * @tc.require: AR000CCPOL + * @tc.author: liqiao + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreSubscribeKvStore007, TestSize.Level0) +{ + // cover app_kvstore_subscribekvstore_014, app_kvstore_subscribekvstore_015, + // app_kvstore_subscribekvstore_local_014, app_kvstore_subscribekvstore_local_015 + std::unique_ptr appKvStorePtr; + Status status; + + status = manager->GetKvStore( + options, storeId, [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_NE(appKvStorePtr, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + ObserverImpl *observerLocal = new ObserverImpl(); + status = appKvStorePtr->UnSubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::STORE_NOT_SUBSCRIBE); + ObserverImpl *observerSync = new ObserverImpl(); + status = appKvStorePtr->UnSubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::STORE_NOT_SUBSCRIBE); + + status = appKvStorePtr->SubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->SubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->UnSubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::SUCCESS); + status = appKvStorePtr->UnSubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::SUCCESS); + + status = appKvStorePtr->UnSubscribeKvStore(localRead, subscribeType, observerLocal); + EXPECT_EQ(status, Status::STORE_NOT_SUBSCRIBE); + status = appKvStorePtr->UnSubscribeKvStore(syncRead, subscribeType, observerSync); + EXPECT_EQ(status, Status::STORE_NOT_SUBSCRIBE); + + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + + status = manager->DeleteKvStore(storeId); + EXPECT_EQ(status, Status::SUCCESS); + delete observerLocal; + delete observerSync; + observerLocal = nullptr; + observerSync = nullptr; +} + +static void InitResultSetData() +{ + std::unique_ptr appKvStorePtr; + Status status = AppDistributedKvStoreTest::manager->GetKvStore( + AppDistributedKvStoreTest::options, "school", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + WriteOptions localWrite; + localWrite.local = false; + const std::vector students = {"stu_Id1", "stu_Id2", "stu_Id3", "stu_Id4", "stu_Id5"}; + for (const auto &student : students) { + status = appKvStorePtr->Put(localWrite, Key(student), Value("result_set")); + EXPECT_EQ(status, Status::SUCCESS); + } + const std::vector teachers = {"tch_Id1", "tch_Id2", "tch_Id3", "tch_Id4", "tch_Id5", "tch_Id6"}; + for (const auto &teacher : teachers) { + status = appKvStorePtr->Put(localWrite, Key(teacher), Value("result_set")); + EXPECT_EQ(status, Status::SUCCESS); + } + AppDistributedKvStoreTest::manager->CloseKvStore(std::move(appKvStorePtr)); +} + +/** + * @tc.name: AppKvstoreResultSet001 + * @tc.desc: test ResultSet GetEntries when key not exist + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: liuyuhui + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreResultSet001, TestSize.Level0) +{ + /** + * @tc.steps: step1. initialize kvstore. + * @tc.expected: step1. SUCCESS. + */ + std::unique_ptr appKvStorePtr; + Status status = manager->GetKvStore( + options, "school", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + /** + * @tc.steps: step2. call GetEntries when key not exist. + * @tc.expected: step2. KEY_NOT_FOUND. + */ + AppKvStoreResultSet *appKvStoreResultSet = nullptr; + status = appKvStorePtr->GetEntries(Key("key_no_exist"), appKvStoreResultSet); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_NE(appKvStoreResultSet, nullptr); + EXPECT_EQ(0, appKvStoreResultSet->GetCount()); + status = appKvStorePtr->CloseResultSet(appKvStoreResultSet); + EXPECT_EQ(status, Status::SUCCESS); + + /** + * @tc.steps: step3. close and delete kvstore. + * @tc.expected: step3. SUCCESS. + */ + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("school"); + EXPECT_EQ(status, Status::SUCCESS); +} + +/** + * @tc.name: AppKvstoreResultSet002 + * @tc.desc: test ResultSet GetEntries + * @tc.type: FUNC + * @tc.require: AR000D08KT + * @tc.author: liuyuhui + */ +HWTEST_F(AppDistributedKvStoreTest, AppKvstoreResultSet002, TestSize.Level0) +{ + InitResultSetData(); + + /** + * @tc.steps: step1. initialize kvstore. + * @tc.expected: step1. SUCCESS. + */ + std::unique_ptr appKvStorePtr; + Status status = manager->GetKvStore( + options, "school", [&](std::unique_ptr appKvStore) { + appKvStorePtr = std::move(appKvStore); + }); + EXPECT_EQ(status, Status::SUCCESS); + + /** + * @tc.steps: step2. call GetEntries with param appKvStoreResultSet. + * @tc.expected: step2. SUCCESS. + */ + AppKvStoreResultSet *appKvStoreResultSet = nullptr; + status = appKvStorePtr->GetEntries(Key("stu_"), appKvStoreResultSet); + EXPECT_NE(appKvStoreResultSet, nullptr); + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(5, static_cast(appKvStoreResultSet->GetCount())); + + EXPECT_EQ(-1, static_cast(appKvStoreResultSet->GetPosition())); + EXPECT_EQ(true, appKvStoreResultSet->IsBeforeFirst()); + + EXPECT_EQ(true, appKvStoreResultSet->MoveToNext()); + EXPECT_EQ(0, static_cast(appKvStoreResultSet->GetPosition())); + EXPECT_EQ(true, appKvStoreResultSet->IsFirst()); + + EXPECT_EQ(true, appKvStoreResultSet->Move(1)); + EXPECT_EQ(1, static_cast(appKvStoreResultSet->GetPosition())); + EXPECT_EQ(false, appKvStoreResultSet->IsFirst()); + + EXPECT_EQ(true, appKvStoreResultSet->MoveToPosition(2)); + EXPECT_EQ(2, static_cast(appKvStoreResultSet->GetPosition())); + + EXPECT_EQ(true, appKvStoreResultSet->MoveToPrevious()); + EXPECT_EQ(1, static_cast(appKvStoreResultSet->GetPosition())); + + EXPECT_EQ(false, appKvStoreResultSet->IsAfterLast()); + EXPECT_EQ(true, appKvStoreResultSet->MoveToLast()); + EXPECT_EQ(4, static_cast(appKvStoreResultSet->GetPosition())); + EXPECT_EQ(true, appKvStoreResultSet->IsLast()); + Entry entry; + status = appKvStoreResultSet->GetEntry(entry); + EXPECT_EQ(Status::SUCCESS, status); + EXPECT_EQ(entry.key.ToString(), std::string("stu_Id5")); + EXPECT_EQ(entry.value.ToString(), std::string("result_set")); + + EXPECT_EQ(false, appKvStoreResultSet->MoveToNext()); + EXPECT_EQ(true, appKvStoreResultSet->IsAfterLast()); + + EXPECT_EQ(false, appKvStoreResultSet->MoveToNext()); + EXPECT_EQ(true, appKvStoreResultSet->IsAfterLast()); + + EXPECT_EQ(false, appKvStoreResultSet->MoveToPosition(5)); // MoveToPosition more than data size + EXPECT_EQ(false, appKvStoreResultSet->Move(10)); // Move more than data size + + status = appKvStorePtr->CloseResultSet(appKvStoreResultSet); + EXPECT_EQ(status, Status::SUCCESS); + + /** + * @tc.steps: step3. close and delete kvstore. + * @tc.expected: step3. SUCCESS. + */ + status = manager->CloseKvStore(std::move(appKvStorePtr)); + EXPECT_EQ(status, Status::SUCCESS); + status = manager->DeleteKvStore("school"); + EXPECT_EQ(status, Status::SUCCESS); +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/blob_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/blob_test.cpp new file mode 100755 index 000000000..dd3a46634 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/blob_test.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "types.h" +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class BlobTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void BlobTest::SetUpTestCase(void) +{} + +void BlobTest::TearDownTestCase(void) +{} + +void BlobTest::SetUp(void) +{} + +void BlobTest::TearDown(void) +{} + +/** +* @tc.name: Size001 +* @tc.desc: construct a Blob and check its size. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Size001, TestSize.Level0) +{ + Blob blob1; + EXPECT_EQ(blob1.Size(), (size_t)0); + Blob blob2 = "1234567890"; + EXPECT_EQ(blob2.Size(), (size_t)10); + Blob blob3("12345"); + EXPECT_EQ(blob3.Size(), (size_t)5); + std::string strTmp = "123"; + const char *chr = strTmp.c_str(); + Blob blob4(chr); + EXPECT_EQ(blob4.Size(), (size_t)3); + std::vector vec = {'1', '2', '3', '4'}; + Blob blob5(vec); + EXPECT_EQ(blob5.Size(), (size_t)4); + const char *chr1 = strTmp.c_str(); + Blob blob6(chr1, strlen(chr1)); + EXPECT_EQ(blob6.Size(), (size_t)3); +} + +/** +* @tc.name: Empty001 +* @tc.desc: construct a Blob and check its empty. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Empty001, TestSize.Level0) +{ + Blob blob1; + EXPECT_EQ(blob1.Empty(), true); + Blob blob2 = "1234567890"; + EXPECT_EQ(blob2.Empty(), false); + Blob blob3("12345"); + EXPECT_EQ(blob3.Empty(), false); + std::string strTmp = "123"; + const char *chr = strTmp.c_str(); + Blob blob4(chr); + EXPECT_EQ(blob4.Empty(), false); + std::vector vec = {'1', '2', '3', '4'}; + Blob blob5(vec); + EXPECT_EQ(blob5.Empty(), false); + const char *chr1 = strTmp.c_str(); + Blob blob6(chr1, strlen(chr1)); + EXPECT_EQ(blob6.Empty(), false); +} + +/** +* @tc.name: Clear001 +* @tc.desc: construct a Blob and check it clear function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Clear001, TestSize.Level0) +{ + Blob blob1 = "1234567890"; + blob1.Clear(); + EXPECT_EQ(blob1.Empty(), true); + Blob blob2("12345"); + blob2.Clear(); + EXPECT_EQ(blob2.Empty(), true); + std::string strTmp = "123"; + const char *chr = strTmp.c_str(); + Blob blob3(chr); + blob3.Clear(); + EXPECT_EQ(blob3.Empty(), true); + std::vector vec = {'1', '2', '3', '4'}; + Blob blob4(vec); + blob4.Clear(); + EXPECT_EQ(blob4.Empty(), true); +} + +/** +* @tc.name: StartsWith001 +* @tc.desc: construct a Blob and check it StartsWith function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, StartsWith001, TestSize.Level0) +{ + Blob blob1 = "1234567890"; + Blob blob2("12345"); + EXPECT_EQ(blob1.StartsWith(blob2), true); + EXPECT_EQ(blob2.StartsWith(blob1), false); + Blob blob3("234"); + EXPECT_EQ(blob1.StartsWith(blob3), false); + EXPECT_EQ(blob2.StartsWith(blob3), false); +} + +/** +* @tc.name: Compare001 +* @tc.desc: construct a Blob and check it compare function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Compare001, TestSize.Level0) +{ + Blob blob1 = "1234567890"; + Blob blob2("12345"); + EXPECT_EQ(blob1.Compare(blob2), 1); + EXPECT_EQ(blob2.Compare(blob1), -1); + Blob blob3("12345"); + EXPECT_EQ(blob2.Compare(blob3), 0); +} + +/** +* @tc.name: Data001 +* @tc.desc: construct a Blob and check it Data function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Data001, TestSize.Level0) +{ + std::vector result = {'1', '2', '3', '4'}; + Blob blob1("1234"); + EXPECT_EQ(blob1.Data(), result); + std::vector result2 = {'1', '2', '3', '4', '5'}; + Blob blob2("12345"); + EXPECT_EQ(blob2.Data(), result2); +} + +/** +* @tc.name: ToString001 +* @tc.desc: construct a Blob and check it ToString function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, ToString001, TestSize.Level0) +{ + Blob blob1("1234"); + std::string str = "1234"; + EXPECT_EQ(blob1.ToString(), str); +} + +/** +* @tc.name: OperatorEqual001 +* @tc.desc: construct a Blob and check it operator== function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, OperatorEqual001, TestSize.Level0) +{ + Blob blob1("1234"); + Blob blob2("1234"); + EXPECT_EQ(blob1 == blob2, true); + Blob blob3("12345"); + EXPECT_EQ(blob1 == blob3, false); +} + +/** +* @tc.name: Operator001 +* @tc.desc: construct a Blob and check it operator[] function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Operator001, TestSize.Level0) +{ + Blob blob1("1234"); + EXPECT_EQ(blob1[0], '1'); + EXPECT_EQ(blob1[1], '2'); + EXPECT_EQ(blob1[2], '3'); + EXPECT_EQ(blob1[3], '4'); + EXPECT_EQ(blob1[4], 0); +} + +/** +* @tc.name: Operator002 +* @tc.desc: construct a Blob and check it operator= function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Operator002, TestSize.Level0) +{ + Blob blob1("1234"); + Blob blob2 = blob1; + EXPECT_EQ(blob1 == blob2, true); + EXPECT_EQ(blob2.ToString(), "1234"); +} + +/** +* @tc.name: Operator003 +* @tc.desc: construct a Blob and check it operator= function. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liqiao +*/ +HWTEST_F(BlobTest, Operator003, TestSize.Level0) +{ + Blob blob1("1234"); + Blob blob2 = std::move(blob1); + EXPECT_EQ(blob1 == blob2, false); + EXPECT_EQ(blob1.Empty(), true); + EXPECT_EQ(blob2.ToString(), "1234"); +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_encrypt_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_encrypt_test.cpp new file mode 100755 index 000000000..b6bbc4a51 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_encrypt_test.cpp @@ -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. + */ + +#define LOG_TAG "DistributedKvDataManagerEncryptTest" + +#include "distributed_kv_data_manager.h" +#include "kvstore_death_recipient.h" +#include +#include +#include +#include "types.h" +#include "log_print.h" +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class DistributedKvDataManagerEncryptTest : public testing::Test { +public: + static DistributedKvDataManager manager; + static Options createEnc; + + static UserId userId; + + static AppId appId; + static StoreId storeId; + + static void SetUpTestCase(void); + static void TearDownTestCase(void); + + static void RemoveAllStore(DistributedKvDataManager manager); + + void SetUp(); + void TearDown(); + DistributedKvDataManagerEncryptTest(); + virtual ~DistributedKvDataManagerEncryptTest(); +}; + +class MyDeathRecipient : public KvStoreDeathRecipient { +public: + MyDeathRecipient() {} + virtual ~MyDeathRecipient() {} + void OnRemoteDied() override {} +}; + +DistributedKvDataManager DistributedKvDataManagerEncryptTest::manager; +Options DistributedKvDataManagerEncryptTest::createEnc; + +UserId DistributedKvDataManagerEncryptTest::userId; + +AppId DistributedKvDataManagerEncryptTest::appId; +StoreId DistributedKvDataManagerEncryptTest::storeId; + +void DistributedKvDataManagerEncryptTest::RemoveAllStore(DistributedKvDataManager manager) +{ + manager.CloseAllKvStore(appId); + manager.DeleteKvStore(appId, storeId); + manager.DeleteAllKvStore(appId); +} +void DistributedKvDataManagerEncryptTest::SetUpTestCase(void) +{ + createEnc.createIfMissing = true; + createEnc.encrypt = true; + createEnc.autoSync = true; + + userId.userId = "account0"; + appId.appId = "com.ohos.nb.service"; + + storeId.storeId = "EncryptStoreId"; +} + +void DistributedKvDataManagerEncryptTest::TearDownTestCase(void) +{ + RemoveAllStore(manager); +} + +void DistributedKvDataManagerEncryptTest::SetUp(void) +{} + +DistributedKvDataManagerEncryptTest::DistributedKvDataManagerEncryptTest(void) +{} + +DistributedKvDataManagerEncryptTest::~DistributedKvDataManagerEncryptTest(void) +{} + +void DistributedKvDataManagerEncryptTest::TearDown(void) +{} + +/** +* @tc.name: kvstore_ddm_createEncryptedStore_001 +* @tc.desc: Create an encrypted KvStore. +* @tc.type: FUNC +* @tc.require: SR000D08K4 AR000D08KQ +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerEncryptTest, kvstore_ddm_createEncryptedStore_001, TestSize.Level0) +{ + ZLOGI("kvstore_ddm_createEncryptedStore_001 begin."); + std::unique_ptr kvStorePtr; + manager.GetKvStore(createEnc, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr, nullptr); + + Key key = "age"; + Value value = "18"; + Status status = kvStorePtr->Put(key, value); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + ASSERT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusRet) << "KvStoreSnapshot get data return wrong status"; + + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_test.cpp new file mode 100755 index 000000000..7bc015b03 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/distributed_kv_data_manager_test.cpp @@ -0,0 +1,846 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DistributedKvDataManagerTest" + +#include "distributed_kv_data_manager.h" +#include +#include +#include +#include "kvstore_death_recipient.h" +#include "log_print.h" +#include "types.h" +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class DistributedKvDataManagerTest : public testing::Test { +public: + static DistributedKvDataManager manager; + static Options create; + static Options noCreate; + + static UserId userId; + + static AppId appId; + static StoreId storeId64; + static StoreId storeId65; + static StoreId storeIdTest; + static StoreId storeIdEmpty; + + static Entry entryA; + static Entry entryB; + + static void SetUpTestCase(void); + static void TearDownTestCase(void); + + static void RemoveAllStore(DistributedKvDataManager manager); + + void SetUp(); + void TearDown(); + DistributedKvDataManagerTest(); +}; + +class MyDeathRecipient : public KvStoreDeathRecipient { +public: + MyDeathRecipient() {} + virtual ~MyDeathRecipient() {} + virtual void OnRemoteDied() override {} +}; + +DistributedKvDataManager DistributedKvDataManagerTest::manager; +Options DistributedKvDataManagerTest::create; +Options DistributedKvDataManagerTest::noCreate; + +UserId DistributedKvDataManagerTest::userId; + +AppId DistributedKvDataManagerTest::appId; +StoreId DistributedKvDataManagerTest::storeId64; +StoreId DistributedKvDataManagerTest::storeId65; +StoreId DistributedKvDataManagerTest::storeIdTest; +StoreId DistributedKvDataManagerTest::storeIdEmpty; + +Entry DistributedKvDataManagerTest::entryA; +Entry DistributedKvDataManagerTest::entryB; + +void DistributedKvDataManagerTest::RemoveAllStore(DistributedKvDataManager manager) +{ + manager.CloseAllKvStore(appId); + manager.DeleteAllKvStore(appId); +} +void DistributedKvDataManagerTest::SetUpTestCase(void) +{ + create.createIfMissing = true; + create.encrypt = false; + create.autoSync = true; + + noCreate.createIfMissing = false; + noCreate.encrypt = false; + noCreate.autoSync = true; + noCreate.dataOwnership = true; + + userId.userId = "account0"; + appId.appId = "com.ohos.kvdatamanager.test"; + + storeId64.storeId = "a000000000b000000000c000000000d000000000e000000000f000000000g000"; + storeId65.storeId = "a000000000b000000000c000000000d000000000e000000000f000000000g000" + "a000000000b000000000c000000000d000000000e000000000f000000000g0000"; + storeIdTest.storeId = "test"; + storeIdEmpty.storeId = ""; + + entryA.key = "a"; + entryA.value = "valueA"; + entryB.key = "b"; + entryB.value = "valueB"; + RemoveAllStore(manager); +} + +void DistributedKvDataManagerTest::TearDownTestCase(void) +{ + RemoveAllStore(manager); +} + +void DistributedKvDataManagerTest::SetUp(void) +{} + +DistributedKvDataManagerTest::DistributedKvDataManagerTest(void) +{} + +void DistributedKvDataManagerTest::TearDown(void) +{ + RemoveAllStore(manager); +} + +/** +* @tc.name: GetKvStore001 +* @tc.desc: Get an exist KvStore +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore001, TestSize.Level0) +{ + ZLOGI("GetKvStore001 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + + std::unique_ptr existKvStorePtr; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr, nullptr); +} + +/** +* @tc.name: GetKvStore002 +* @tc.desc: Create and get a new KvStore +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore002, TestSize.Level0) +{ + ZLOGI("GetKvStore002 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + manager.CloseKvStore(appId, storeId64); + manager.DeleteKvStore(appId, storeId64); +} + +/** +* @tc.name: GetKvStore003 +* @tc.desc: Get a non-existing KvStore, and the callback function should receive STORE_NOT_FOUND and +* get a nullptr. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore003, TestSize.Level0) +{ + ZLOGI("GetKvStore003 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + EXPECT_EQ(status, Status::STORE_NOT_FOUND); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); +} + +/** +* @tc.name: GetKvStore004 +* @tc.desc: Create a KvStore with an empty storeId, and the callback function should receive +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore004, TestSize.Level0) +{ + ZLOGI("GetKvStore004 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeIdEmpty, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::INVALID_ARGUMENT); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); +} + +/** +* @tc.name: GetKvStore005 +* @tc.desc: Get a KvStore with an empty storeId, and the callback function should receive INVALID_ARGUMENT +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore005, TestSize.Level0) +{ + ZLOGI("GetKvStore005 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(noCreate, appId, storeIdEmpty, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::INVALID_ARGUMENT); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); +} + +/** +* @tc.name: GetKvStore006 +* @tc.desc: Create a KvStore with a 65-byte storeId, and the callback function should receive INVALID_ARGUMENT +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore006, TestSize.Level0) +{ + ZLOGI("GetKvStore006 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId65, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::INVALID_ARGUMENT); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); +} + +/** +* @tc.name: GetKvStore007 +* @tc.desc: Get a KvStore with a 65-byte storeId, the callback function should receive INVALID_ARGUMENT +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetKvStore007, TestSize.Level0) +{ + ZLOGI("GetKvStore007 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(noCreate, appId, storeId65, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::INVALID_ARGUMENT); + }); + EXPECT_EQ(notExistKvStorePtr, nullptr); +} + +/** +* @tc.name: GetAllKvStore001 +* @tc.desc: Get all KvStore IDs when no KvStore exists, and the callback function should receive a 0-length vector. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetAllKvStore001, TestSize.Level0) +{ + ZLOGI("GetAllKvStore001 begin."); + std::vector idList; + manager.GetAllKvStoreId(appId, [&](Status status, std::vector &idList) { + EXPECT_EQ(status, Status::SUCCESS); + EXPECT_EQ(idList.size(), (unsigned long)0); + }); +} + +/** +* @tc.name: GetAllKvStore002 +* @tc.desc: Get all KvStore IDs when no KvStore exists, and the callback function should receive a 0-length vector. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, GetAllKvStore002, TestSize.Level0) +{ + ZLOGI("GetAllKvStore002 begin."); + StoreId id1; + id1.storeId = "id1"; + StoreId id2; + id2.storeId = "id2"; + StoreId id3; + id3.storeId = "id3"; + manager.GetKvStore(create, appId, id1, [&](Status status, std::unique_ptr kvStore) { + ASSERT_NE(kvStore, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + }); + manager.GetKvStore(create, appId, id2, [&](Status status, std::unique_ptr kvStore) { + ASSERT_NE(kvStore, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + }); + manager.GetKvStore(create, appId, id3, [&](Status status, std::unique_ptr kvStore) { + ASSERT_NE(kvStore, nullptr); + ASSERT_EQ(status, Status::SUCCESS); + }); + + std::vector idList; + manager.GetAllKvStoreId(appId, [&](Status status, std::vector &idList) { + EXPECT_EQ(status, Status::SUCCESS); + bool haveId1 = false; + bool haveId2 = false; + bool haveId3 = false; + for (StoreId id : idList) { + if (id.storeId == "id1") { + haveId1 = true; + } else if (id.storeId == "id2") { + haveId2 = true; + } else if (id.storeId == "id3") { + haveId3 = true; + } else { + ZLOGI("got an unknown storeId."); + EXPECT_TRUE(false); + } + } + EXPECT_TRUE(haveId1); + EXPECT_TRUE(haveId2); + EXPECT_TRUE(haveId3); + EXPECT_EQ(idList.size(), (unsigned long)3); + }); +} + +/** +* @tc.name: CloseKvStore001 +* @tc.desc: Close an opened KVStore, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStore001, TestSize.Level0) +{ + ZLOGI("CloseKvStore001 begin."); + std::unique_ptr kvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: CloseKvStore002 +* @tc.desc: Close a closed KvStore, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStore002, TestSize.Level0) +{ + ZLOGI("CloseKvStore002 begin."); + std::unique_ptr kvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr, nullptr); + + manager.CloseKvStore(appId, storeId64); + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::STORE_NOT_OPEN); +} + +/** +* @tc.name: CloseKvStore003 +* @tc.desc: Close a KvStore with an empty storeId, and the callback function should return INVALID_ARGUMENT. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStore003, TestSize.Level0) +{ + ZLOGI("CloseKvStore003 begin."); + Status stat = manager.CloseKvStore(appId, storeIdEmpty); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** +* @tc.name: CloseKvStore004 +* @tc.desc: Close a KvStore with a 65-byte storeId, and the callback function should return INVALID_ARGUMENT. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStore004, TestSize.Level0) +{ + ZLOGI("CloseKvStore004 begin."); + Status stat = manager.CloseKvStore(appId, storeId65); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** +* @tc.name: CloseKvStore005 +* @tc.desc: Close a non-existing KvStore, and the callback function should return STORE_NOT_OPEN. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStore005, TestSize.Level0) +{ + ZLOGI("CloseKvStore005 begin."); + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::STORE_NOT_OPEN); +} + +/** +* @tc.name: CloseKvStoreMulti001 +* @tc.desc: Open a KvStore several times and close them one by one, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000CSKRU +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStoreMulti001, TestSize.Level0) +{ + ZLOGI("CloseKvStoreMulti001 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + + std::unique_ptr existKvStorePtr; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: CloseKvStoreMulti002 +* @tc.desc: Open a KvStore several times and close them one by one, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000CSKRU +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStoreMulti002, TestSize.Level0) +{ + ZLOGI("CloseKvStoreMulti002 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + + std::unique_ptr existKvStorePtr1; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr1, nullptr); + + std::unique_ptr existKvStorePtr2; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr2, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_NE(stat, Status::SUCCESS); +} + +/** +* @tc.name: CloseKvStoreMulti003 +* @tc.desc: Open a KvStore several times and close them one by one, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000CSKRU +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseKvStoreMulti003, TestSize.Level0) +{ + ZLOGI("CloseKvStoreMulti003 begin."); + std::unique_ptr notExistKvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + notExistKvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(notExistKvStorePtr, nullptr); + + std::unique_ptr existKvStorePtr1; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr1, nullptr); + + std::unique_ptr existKvStorePtr2; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr2, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + Key keyInt = "math_score_int"; + Value valueInt = Value(TransferTypeToByteArray(-100)); + Status status = existKvStorePtr2->Put(keyInt, valueInt); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + existKvStorePtr2->GetKvStoreSnapshot(nullptr, + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + + std::unique_ptr existKvStorePtr3; + manager.GetKvStore(noCreate, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + existKvStorePtr3 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + EXPECT_NE(existKvStorePtr3, nullptr); + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + Value valueRetInt; + Status statusTmp = kvStoreSnapshotPtr->Get(keyInt, valueRetInt); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ(valueInt, valueRetInt) << "valueInt and valueRetInt are not equal"; + + stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: CloseAllKvStore001 +* @tc.desc: Close all opened KvStores, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseAllKvStore001, TestSize.Level0) +{ + ZLOGI("CloseAllKvStore001 begin."); + std::unique_ptr kvStorePtr1; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr1, nullptr); + + std::unique_ptr kvStorePtr2; + manager.GetKvStore(create, appId, storeIdTest, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr2, nullptr); + + Status stat = manager.CloseAllKvStore(appId); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: CloseAllKvStore002 +* @tc.desc: Close all KvStores which exist but are not opened, and the callback function should return STORE_NOT_OPEN. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, CloseAllKvStore002, TestSize.Level0) +{ + ZLOGI("CloseAllKvStore002 begin."); + std::unique_ptr kvStorePtr1; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr1, nullptr); + + std::unique_ptr kvStorePtr2; + manager.GetKvStore(create, appId, storeIdTest, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr2, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.CloseAllKvStore(appId); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: DeleteKvStore001 +* @tc.desc: Delete a closed KvStore, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteKvStore001, TestSize.Level0) +{ + ZLOGI("DeleteKvStore001 begin."); + std::unique_ptr kvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr, nullptr); + + Status stat = manager.CloseKvStore(appId, storeId64); + ASSERT_EQ(stat, Status::SUCCESS); + + stat = manager.DeleteKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: DeleteKvStore002 +* @tc.desc: Delete an opened KvStore, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteKvStore002, TestSize.Level0) +{ + ZLOGI("DeleteKvStore002 begin."); + std::unique_ptr kvStorePtr; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr, nullptr); + + // first close it if opened, and then delete it. + Status stat = manager.DeleteKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: DeleteKvStore003 +* @tc.desc: Delete a non-existing KvStore, and the callback function should return DB_ERROR. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteKvStore003, TestSize.Level0) +{ + ZLOGI("DeleteKvStore003 begin."); + Status stat = manager.DeleteKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::DB_ERROR); +} + +/** +* @tc.name: DeleteKvStore004 +* @tc.desc: Delete a KvStore with an empty storeId, and the callback function should return INVALID_ARGUMENT. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteKvStore004, TestSize.Level0) +{ + ZLOGI("DeleteKvStore004 begin."); + Status stat = manager.DeleteKvStore(appId, storeIdEmpty); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** +* @tc.name: DeleteKvStore005 +* @tc.desc: Delete a KvStore with 65 bytes long storeId (which exceed storeId length limit). Should +* return INVALID_ARGUMENT. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteKvStore005, TestSize.Level0) +{ + ZLOGI("DeleteKvStore005 begin."); + Status stat = manager.DeleteKvStore(appId, storeId65); + EXPECT_EQ(stat, Status::INVALID_ARGUMENT); +} + +/** +* @tc.name: DeleteAllKvStore001 +* @tc.desc: Delete all KvStores after closing all of them, and the callback function should return SUCCESS. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteAllKvStore001, TestSize.Level0) +{ + ZLOGI("DeleteAllKvStore001 begin."); + std::unique_ptr kvStorePtr1; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr1, nullptr); + std::unique_ptr kvStorePtr2; + manager.GetKvStore(create, appId, storeIdTest, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr2, nullptr); + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + stat = manager.CloseKvStore(appId, storeIdTest); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.DeleteAllKvStore(appId); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: DeleteAllKvStore002 +* @tc.desc: Delete all kvstore fail when any kvstore in the appId is not closed +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteAllKvStore002, TestSize.Level0) +{ + ZLOGI("DeleteAllKvStore002 begin."); + std::unique_ptr kvStorePtr1; + manager.GetKvStore(create, appId, storeId64, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr1 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr1, nullptr); + std::unique_ptr kvStorePtr2; + manager.GetKvStore(create, appId, storeIdTest, [&](Status status, std::unique_ptr kvStore) { + kvStorePtr2 = std::move(kvStore); + ASSERT_EQ(status, Status::SUCCESS); + }); + ASSERT_NE(kvStorePtr2, nullptr); + Status stat = manager.CloseKvStore(appId, storeId64); + EXPECT_EQ(stat, Status::SUCCESS); + + stat = manager.DeleteAllKvStore(appId); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: DeleteAllKvStore003 +* @tc.desc: Delete all KvStores even if no KvStore exists in the appId. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000BVTDM +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, DeleteAllKvStore003, TestSize.Level0) +{ + ZLOGI("DeleteAllKvStore003 begin."); + Status stat = manager.DeleteAllKvStore(appId); + EXPECT_EQ(stat, Status::SUCCESS); +} + +/** +* @tc.name: RegisterKvStoreServiceDeathRecipient001 +* @tc.desc: Register a callback called when the service dies. +* @tc.type: FUNC +* @tc.require: SR000CQDU0 AR000CQDU1 +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, RegisterKvStoreServiceDeathRecipient001, TestSize.Level0) +{ + ZLOGI("RegisterKvStoreServiceDeathRecipient001 begin."); + std::shared_ptr kvStoreDeathRecipientPtr = std::make_shared(); + manager.RegisterKvStoreServiceDeathRecipient(kvStoreDeathRecipientPtr); + kvStoreDeathRecipientPtr->OnRemoteDied(); +} + +/** +* @tc.name: UnRegisterKvStoreServiceDeathRecipient001 +* @tc.desc: Unregister the callback called when the service dies. +* @tc.type: FUNC +* @tc.require: AR000CQDUS AR000CQDU1 +* @tc.author: liqiao +*/ +HWTEST_F(DistributedKvDataManagerTest, UnRegisterKvStoreServiceDeathRecipient001, TestSize.Level0) +{ + ZLOGI("UnRegisterKvStoreServiceDeathRecipient001 begin."); + std::shared_ptr kvStoreDeathRecipientPtr = std::make_shared(); + manager.UnRegisterKvStoreServiceDeathRecipient(kvStoreDeathRecipientPtr); +} + +class DeviceListenerImpl : public DeviceStatusChangeListener { +public: + void OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const override + { + } + DeviceFilterStrategy GetFilterStrategy() const override + { + return DeviceFilterStrategy::NO_FILTER; + } +}; +/** +* @tc.name: GetDevice001 +* @tc.desc: Get device id. +* @tc.type: FUNC +* @tc.require: SR000DOH1R AR000DPSGU +* @tc.author: hongbo +*/ +HWTEST_F(DistributedKvDataManagerTest, GetDevice001, TestSize.Level0) +{ + ZLOGI("GetDevice001 begin."); + DeviceInfo info; + Status status = manager.GetLocalDevice(info); + EXPECT_EQ(Status::SUCCESS, status) << "expected getLocalDevice true"; + EXPECT_TRUE(info.deviceId.size() > 0) << "expected deviceId exist"; + + std::vector infos; + status = manager.GetDeviceList(infos, DeviceFilterStrategy::FILTER); + // EXPECT_EQ(Status::SUCCESS, status) << "expected GetDeviceList true"; + // EXPECT_TRUE(infos.size() == 0) << "expected GetDeviceList exist"; + + auto listener = std::make_shared(); + status = manager.StartWatchDeviceChange(listener); + EXPECT_EQ(Status::SUCCESS, status) << "expected StartWatchDeviceChange true"; + status = manager.StopWatchDeviceChange(listener); + EXPECT_EQ(Status::SUCCESS, status) << "expected StopWatchDeviceChange true"; +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_client_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_client_test.cpp new file mode 100644 index 000000000..99097e616 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_client_test.cpp @@ -0,0 +1,1354 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreClientTest" + +#include +#include +#include +#include +#include +#include "distributed_kv_data_manager.h" +#include "log_print.h" +#include "types.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class KvStoreClientTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + static std::unique_ptr kvStorePtr; // declare kvstore instance. + static std::unique_ptr kvStoreSnapshotPtr; // declare kvstore instance. + static Status statusGetKvStore; + static Status statusGetSnapshot; + static int MAX_VALUE_SIZE; +}; + +std::unique_ptr KvStoreClientTest::kvStorePtr = nullptr; +std::unique_ptr KvStoreClientTest::kvStoreSnapshotPtr = nullptr; +Status KvStoreClientTest::statusGetKvStore = Status::ERROR; +Status KvStoreClientTest::statusGetSnapshot = Status::ERROR; +int KvStoreClientTest::MAX_VALUE_SIZE = 4 * 1024 * 1024; + +void KvStoreClientTest::SetUpTestCase(void) +{ + DistributedKvDataManager manager; + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "odmf"; // define app name. + StoreId storeId; + storeId.storeId = "student"; // define kvstore(database) name. + + manager.CloseAllKvStore(appId); + manager.DeleteAllKvStore(appId); + + // [create and] open and initialize kvstore instance. + manager.GetKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + kvStorePtr = std::move(kvStore); + }); + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + statusGetSnapshot = status; + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); +} + +void KvStoreClientTest::TearDownTestCase(void) +{} + +void KvStoreClientTest::SetUp(void) +{} + +void KvStoreClientTest::TearDown(void) +{} + +/** +* @tc.name: KvStoreDdmGetKvStoreSnapshot001 +* @tc.desc: Get the KvStore snapshot. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmGetKvStoreSnapshot001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmGetKvStoreSnapshot001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "name"; + Value value = "test"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get values from KvStore before putting them into KvStore. + Value valueRet; + Status statusRet1 = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet1) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ("", valueRet.ToString()) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); + + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get values from KvStore after putting them into KvStore. + Status statusRet2 = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut001 +* @tc.desc: Put int values to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store int value to kvstore. + Key keyInt = "math_score_int"; + int scoreInt = -383468; + Value valueInt = Value(TransferTypeToByteArray(scoreInt)); + Status status = kvStorePtr->Put(keyInt, valueInt); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get int value from kvstore. + Value valueRetInt; + Status statusTmp = kvStoreSnapshotPtr->Get(keyInt, valueRetInt); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(valueInt, valueRetInt) << "valueInt and valueRetInt are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut002 +* @tc.desc: Put Float values to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store float value to kvstore. + Key keyFloat = "math_score_float"; + float scoreFloat = 3.14f; + Value valueFloat = Value(TransferTypeToByteArray(scoreFloat)); + Status status = kvStorePtr->Put(keyFloat, valueFloat); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get float value from kvstore. + Value valueRetFloat; + Status statusTmp = kvStoreSnapshotPtr->Get(keyFloat, valueRetFloat); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(valueFloat, valueRetFloat) << "valueFloat and valueRetFloat are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut003 +* @tc.desc: Put Double values to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut003, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store double value to kvstore. + Key keyDouble = "math_score_double"; + double scoreDouble = 28.785f; + Value valueDouble = Value(TransferTypeToByteArray(scoreDouble)); + Status status = kvStorePtr->Put(keyDouble, valueDouble); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get double value from kvstore. + Value valueRetDouble; + Status statusTmp = kvStoreSnapshotPtr->Get(keyDouble, valueRetDouble); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(valueDouble, valueRetDouble) << "valueDouble and valueRetDouble are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut004 +* @tc.desc: put unsigned int value to kvstore +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut004, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store unsigned int value to kvstore. + Key keyUInt = "math_score_size_t"; + size_t scoreUInt = 28; + Value valueUInt = Value(TransferTypeToByteArray(scoreUInt)); + Status status = kvStorePtr->Put(keyUInt, valueUInt); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get unsigned int value from kvstore. + Value valueRetUInt; + Status statusTmp = kvStoreSnapshotPtr->Get(keyUInt, valueRetUInt); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(valueUInt, valueRetUInt) << "valueUInt and valueRetUInt are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut005 +* @tc.desc: Put Long values to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut005, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store long int value to kvstore. + Key keyInt64 = "math_score_int64_t"; + std::int64_t scoreInt64 = 12345678; + Value valueInt64 = Value(TransferTypeToByteArray(scoreInt64)); + Status status = kvStorePtr->Put(keyInt64, valueInt64); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get long int value from kvstore. + Value valueRetint64; + Status statusTmp = kvStoreSnapshotPtr->Get(keyInt64, valueRetint64); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(valueInt64, valueRetint64) << "valueInt64 and valueRetint64 are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut006 +* @tc.desc: Put JSON values to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut006, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut006 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store json value to kvstore. + Key key = "student_name_zhangsan"; + Value value = "{\"class\":20, \"age\":18, \"gradle\":\"good\"}"; + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get json value from kvstore. + Value valueRet; + Status statusTmp = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut007 +* @tc.desc: Put strings containing '\0' to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut007, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut007 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store normal string to kvstore. + Key key = "teacher_name_wanger"; + std::string str = "class:20\0, age:50"; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get string value from kvstore. + Value valueRet; + Status statusTmp = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut008 +* @tc.desc: Put normal strings to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut008, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut008 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store normal string to kvstore. + Key key = "teacher_name_wanger"; + std::string str = "class:20, age:50"; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get string value from kvstore. + Value valueRet; + Status statusTmp = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut009 +* @tc.desc: Update strings in KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut009, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut009 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "student_name_lisi"; + std::string str1 = "age:18"; + Value value1 = Value(str1); + Status status1 = kvStorePtr->Put(key, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore put data failed, wrong status"; + + std::string str2 = "age:20"; + Value value2 = Value(str2); + Status status2 = kvStorePtr->Put(key, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore put data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get string value from kvstore. + Value valueRet; + Status statusTmp = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(value2, valueRet) << "value2 and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPut010 +* @tc.desc: Put empty keys to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut010, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut010 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = ""; + std::string str = "age:18"; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore put data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPut011 +* @tc.desc: Put empty keys to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut011, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut011 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "student_name_lisi"; + std::string str = ""; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPut012 +* @tc.desc: Generate keys with a length of more than 1024 bytes. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut012, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut012 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // Generate key and the length is more than 1024; + std::string strKey("student"); + for (int i = 0; i < 1024; i++) { + strKey += "a"; + } + Key key = strKey; + + std::string str = "class:2, age:50"; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore put data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPut013 +* @tc.desc: Put keys with only blank space to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut013, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut013 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = " "; + std::string str = "class:2, age:50"; + Value value = Value(str); + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore put data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPut014 +* @tc.desc: Put values greater than MAX_VALUE_SIZE to KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liqiao +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPut014, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPut014 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + // store values greater than MAX_VALUE_SIZE to KvStore. + Key key = "student_name_zhangsan"; + std::vector val(MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + val[i] = static_cast(i); + } + Value value = val; + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data failed, wrong status"; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get values greater than MAX_VALUE_SIZE from KvStore. + Value valueRet; + Status statusTmp = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStoreSnapshot get large data failed, wrong status"; + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDelete001 +* @tc.desc: Delete data which key exists +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDelete001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDelete001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "student_name_lisi"; + std::string str = "age:18"; + Value value = Value(str); + Status status1 = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore put data failed, wrong status"; + + Status status2 = kvStorePtr->Delete(key); // delete data + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore delete data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ("", valueRet.ToString()) << "valueRet EQ fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDelete002 +* @tc.desc: Delete data that contains non-existing keys. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDelete002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDelete002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "student_name_lisi"; + Status status1 = kvStorePtr->Delete(key); // delete data + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore delete data failed, wrong status"; + Status status2 = kvStorePtr->Delete(key); // delete data which not exist + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore delete data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ("", valueRet.ToString()) << "valueRet EQ fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDelete003 +* @tc.desc: Delete data that contains invalid keys. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDelete003, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDelete003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = ""; + Status status = kvStorePtr->Delete(key); // delete data which key is invalid + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore delete data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmClear001 +* @tc.desc: Clear data in KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmClear001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmClear001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key1 = "age"; + Value value1 = "18"; + Status status1 = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore put data failed, wrong status"; + + Key key2 = "name"; + Value value2 = "test"; + Status status2 = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore put data failed, wrong status"; + + Status status3 = kvStorePtr->Clear(); // clear data + EXPECT_EQ(Status::SUCCESS, status3) << "KvStore clear data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Value valueRet1; + Status statusRet1 = kvStoreSnapshotPtr->Get(key1, valueRet1); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet1) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ("", valueRet1.ToString()) << "valueRet EQ fail"; + + Value valueRet2; + Status statusRet2 = kvStoreSnapshotPtr->Get(key2, valueRet2); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet2) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ("", valueRet2.ToString()) << "valueRet EQ fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmClear002 +* @tc.desc: Clearn data in an empty KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmClear002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmClear002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Status status1 = kvStorePtr->Clear(); // clear data + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore clear data failed, wrong status"; + + Status status2 = kvStorePtr->Clear(); // clear data which kvstore is empty + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore clear data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPutBatch001 +* @tc.desc: Put data to KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet1; + Status statusRet1 = kvStoreSnapshotPtr->Get(entry1.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry1.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = kvStoreSnapshotPtr->Get(entry2.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry2.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = kvStoreSnapshotPtr->Get(entry3.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry3.value, valueRet3) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPutBatch002 +* @tc.desc: Update data in KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // before update. + std::vector entriesBefore; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entriesBefore.push_back(entry1); + entriesBefore.push_back(entry2); + entriesBefore.push_back(entry3); + + // after update. + std::vector entriesAfter; + Entry entry4, entry5, entry6; + entry4.key = "student_name_mali"; + entry4.value = "age:20, sex:girl"; + entry5.key = "student_name_caixu"; + entry5.value = "age:19, sex:boy"; + entry6.key = "student_name_liuyue"; + entry6.value = "age:23, sex:girl"; + entriesAfter.push_back(entry4); + entriesAfter.push_back(entry5); + entriesAfter.push_back(entry6); + + Status status = kvStorePtr->PutBatch(entriesAfter); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet1; + Status statusRet1 = kvStoreSnapshotPtr->Get(entry4.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry4.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = kvStoreSnapshotPtr->Get(entry5.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry5.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = kvStoreSnapshotPtr->Get(entry6.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry6.value, valueRet3) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmPutBatch003 +* @tc.desc: Put data that contains invalid data to KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch003, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // before update. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = " "; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = " "; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore putbatch data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPutBatch004 +* @tc.desc: Put data that contains invalid data to KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch004, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // before update. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = ""; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = ""; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore putbatch data failed, wrong status"; +} + +std::string Generate1025KeyLen() +{ + // Generate key and the length is more than 1024; + std::string str("prefix"); + for (int i = 0; i < 1024; i++) { + str += "a"; + } + return str; +} +/** +* @tc.name: KvStoreDdmPutBatch005 +* @tc.desc: Put data that contains invalid data to KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch005, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // before update. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = Generate1025KeyLen(); + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore putbatch data failed, wrong status"; +} + +/** +* @tc.name: KvStoreDdmPutBatch006 +* @tc.desc: Put large data to KvStore in batch. +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmPutBatch006, TestSize.Level2) +{ + ZLOGI("KvStoreDdmPutBatch006 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + std::vector val(MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + val[i] = static_cast(i); + } + Value value = val; + + // before update. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "ruby"; + entry1.value = value; + entry2.key = "weiss"; + entry2.value = value; + entry3.key = "blake"; + entry3.value = value; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data failed, wrong status"; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet1; + Status statusRet1 = kvStoreSnapshotPtr->Get(entry1.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry1.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = kvStoreSnapshotPtr->Get(entry2.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry2.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = kvStoreSnapshotPtr->Get(entry3.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "KvStoreSnapshot get data failed, wrong status"; + EXPECT_EQ(entry3.value, valueRet3) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDeleteBatch001 +* @tc.desc: delete batch data normally +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDeleteBatch001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDeleteBatch001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("student_name_mali"); + keys.push_back("student_name_caixu"); + keys.push_back("student_name_liuyue"); + + Status status1 = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data failed, wrong status"; + + Status status2 = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore deletebatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + students = std::move(entries); + }); + EXPECT_EQ(0, static_cast(students.size())) << "KvStore is not empty, deletebatch fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDeleteBatch002 +* @tc.desc: delete batch data which some keys are not in kvstore +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDeleteBatch002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDeleteBatch002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("student_name_mali"); + keys.push_back("student_name_caixu"); + keys.push_back("student_name_liuyue"); + keys.push_back("student_not_exist"); + + Status status1 = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data failed, wrong status"; + + Status status2 = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore deletebatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + students = std::move(entries); + }); + EXPECT_EQ(0, static_cast(students.size())) << "KvStore is not empty, deletebatch fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDeleteBatch003 +* @tc.desc: delete batch data which some keys are invalid +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDeleteBatch003, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDeleteBatch003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("student_name_mali"); + keys.push_back("student_name_caixu"); + keys.push_back(""); + + Status status1 = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data failed, wrong status"; + + Status status2 = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "KvStore deletebatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "invalid argument, deletebatch fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDeleteBatch004 +* @tc.desc: delete batch data which some keys are invalid +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDeleteBatch004, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDeleteBatch004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("student_name_mali"); + keys.push_back("student_name_caixu"); + keys.push_back(" "); + + Status status1 = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data failed, wrong status"; + + Status status2 = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "KvStore deletebatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "invalid argument, deletebatch fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmDeleteBatch005 +* @tc.desc: delete batch data which some keys are invalid +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmDeleteBatch005, TestSize.Level2) +{ + ZLOGI("KvStoreDdmDeleteBatch005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("student_name_mali"); + keys.push_back("student_name_caixu"); + Key keyTmp = Generate1025KeyLen(); + keys.push_back(keyTmp); + + Status status1 = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data failed, wrong status"; + + Status status2 = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "KvStore deletebatch data failed, wrong status"; + std::unique_ptr kvStoreSnapshotPtr; + + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "invalid argument, deletebatch fail"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreDdmGetStoreId001 +* @tc.desc: get kvstore id +* @tc.type: FUNC +* @tc.require: AR000C6GBG AR000CQS36 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreClientTest, KvStoreDdmGetStoreId001, TestSize.Level2) +{ + ZLOGI("KvStoreDdmGetStoreId001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore failed, wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + StoreId id = kvStorePtr->GetStoreId(); + EXPECT_EQ("student", id.storeId) << "GetStoreId fail"; +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_snapshot_client_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_snapshot_client_test.cpp new file mode 100755 index 000000000..c92c12b64 --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/kvstore_snapshot_client_test.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreSnapshotClientTest" + +#include +#include +#include +#include +#include +#include "distributed_kv_data_manager.h" +#include "log_print.h" +#include "types.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class KvStoreSnapshotClientTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + static std::unique_ptr kvStorePtr; // declare kv store instance. + static Status statusGetKvStore; + static DistributedKvDataManager manager; +}; + +DistributedKvDataManager KvStoreSnapshotClientTest::manager; +std::unique_ptr KvStoreSnapshotClientTest::kvStorePtr = nullptr; +Status KvStoreSnapshotClientTest::statusGetKvStore = Status::ERROR; + +void KvStoreSnapshotClientTest::SetUpTestCase(void) +{ + ZLOGI("KvStoreSnapshotClientTest::SetUpTestCase"); + Options options; + options.createIfMissing = true; + options.encrypt = false; // not supported yet. + options.autoSync = true; // not supported yet. + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "odmf"; // define app name. + StoreId storeId; + storeId.storeId = "student"; // define kvstore(database) name. + + manager.CloseAllKvStore(appId); + manager.DeleteAllKvStore(appId); + // [create and] open and initialize kvstore instance. + manager.GetKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + kvStorePtr = std::move(kvStore); + }); +} + +void KvStoreSnapshotClientTest::TearDownTestCase(void) +{ + ZLOGI("KvStoreSnapshotClientTest::TearDownTestCase"); + if (kvStorePtr != nullptr) { + AppId appId; + appId.appId = "odmf"; // define app name. + StoreId storeId; + storeId.storeId = "student"; // define kvstore(database) name. + manager.CloseKvStore(appId, storeId); + } +} + +void KvStoreSnapshotClientTest::SetUp(void) +{} + +void KvStoreSnapshotClientTest::TearDown(void) +{} + +/** +* @tc.name: KvStoreSnapshotDdmGet001 +* @tc.desc: Get values of keys in KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGet001, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGet001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "age"; + Value value = "18"; + Status status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::SUCCESS, statusRet) << "KvStoreSnapshot get data return wrong status"; + + EXPECT_EQ(value, valueRet) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGet002 +* @tc.desc: Get key values in KvStore when keys do not exist. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGet002, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGet002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + Key key = "age"; + Value value = "18"; + Status status1 = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore put data return wrong status"; + + Status status2 = kvStorePtr->Delete(key); // delete data + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore delete data return wrong status"; + + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + // get value from kvstore. + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet); + EXPECT_EQ(Status::KEY_NOT_FOUND, statusRet) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ("", valueRet.ToString()) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGet003 +* @tc.desc: Get key values in KvStore when keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGet003, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGet003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + + // get value from kvstore. + Key key; + Value valueRet; + Status statusRet = kvStoreSnapshotPtr->Get(key, valueRet); + EXPECT_EQ(Status::INVALID_ARGUMENT, statusRet); + EXPECT_EQ(Status::INVALID_ARGUMENT, statusRet) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ("", valueRet.ToString()) << "value and valueRet are not equal"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGetEntries001 +* @tc.desc: Get key entries from KvStore. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGetEntries001, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGetEntries001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + // get entries + Key keyPrefixStudent = "student_name_"; + std::vector students; + Key token; + Status statusTmp = Status::ERROR; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + statusTmp = std::move(status); + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "GetEntries failed"; + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStore GetEntries data return wrong status"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGetEntries002 +* @tc.desc: Get key entries from KvStore when the keyPrefix does not exist. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGetEntries002, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGetEntries002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + // get entries + Key keyPrefixStudent = "teacher_name_"; + std::vector students; + Key token; + Status statusTmp = Status::ERROR; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + statusTmp = std::move(status); + students = std::move(entries); + }); + EXPECT_EQ(0, static_cast(students.size())) << "GetEntries fail"; + EXPECT_EQ(Status::KEY_NOT_FOUND, statusTmp) << "KvStore GetEntries data return wrong status"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGetEntries003 +* @tc.desc: Get all key entries from KvStore when the keys are empty. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGetEntries003, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGetEntries003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + // get entries + Key keyPrefixStudent = ""; + std::vector students; + Key token; + Status statusTmp = Status::ERROR; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + statusTmp = std::move(status); + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "GetEntries fail"; + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStore GetEntries data return wrong status"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: KvStoreSnapshotDdmGetEntries004 +* @tc.desc: Get all key entries from KvStore when the keys contain only space blank. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGetEntries004, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGetEntries004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + // get entries + Key keyPrefixStudent = " "; + std::vector students; + Key token; + Status statusTmp = Status::ERROR; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + statusTmp = std::move(status); + students = std::move(entries); + }); + EXPECT_EQ(3, static_cast(students.size())) << "GetEntries fail"; + EXPECT_EQ(Status::SUCCESS, statusTmp) << "KvStore GetEntries data return wrong status"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +std::string Generate1025KeyLen() +{ + // Generate key and the length is more than 1024; + std::string str("prefix"); + for (int i = 0; i < 1024; i++) { + str += "a"; + } + return str; +} + +/** +* @tc.name: KvStoreSnapshotDdmGetEntries005 +* @tc.desc: Get key entries from KvStore when the keyPrefix is invalid. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, KvStoreSnapshotDdmGetEntries005, TestSize.Level0) +{ + ZLOGI("KvStoreSnapshotDdmGetEntries005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "student_name_mali"; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + // get entries + Key keyPrefixStudent = Generate1025KeyLen(); + std::vector students; + Key token; + Status statusTmp = Status::ERROR; + std::unique_ptr kvStoreSnapshotPtr; + // [create and] open and initialize kvstore snapshot instance. + kvStorePtr->GetKvStoreSnapshot(nullptr, /* (KvStoreObserver ) */ + [&](Status status, std::unique_ptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "kvStoreSnapshotPtr is nullptr"; + kvStoreSnapshotPtr->GetEntries(keyPrefixStudent, + [&](Status status, std::vector &entries) { + statusTmp = std::move(status); + students = std::move(entries); + }); + EXPECT_EQ(0, static_cast(students.size())) << "invalid argument, GetEntries fail"; + EXPECT_EQ(Status::INVALID_ARGUMENT, statusTmp) << "KvStore GetEntries data return wrong status"; + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); +} + +/** +* @tc.name: EntryIpcInterfaceTest001 +* @tc.desc: Marshal and unmarshall key entries. +* @tc.type: FUNC +* @tc.require: AR000C6GBG +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreSnapshotClientTest, EntryIpcInterfaceTest001, TestSize.Level0) +{ + ZLOGI("EntryIpcInterfaceTest001 begin."); + Entry entryIn, *entryOut; + entryIn.key = "student_name_mali"; + entryIn.value = "age:20"; + OHOS::Parcel parcel; + entryIn.Marshalling(parcel); + entryOut = Entry::Unmarshalling(parcel); + EXPECT_NE(entryOut, nullptr); + EXPECT_EQ(entryOut->key.ToString(), std::string("student_name_mali")); + EXPECT_EQ(entryOut->value.ToString(), std::string("age:20")); + delete entryOut; +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/local_subscribe_store_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/local_subscribe_store_test.cpp new file mode 100755 index 000000000..be45fb3ed --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/local_subscribe_store_test.cpp @@ -0,0 +1,2346 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LocalSubscribeStoreTest" + +#include +#include +#include +#include +#include +#include "distributed_kv_data_manager.h" +#include "log_print.h" +#include "types.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +namespace { +const int USLEEP_TIME = 2000000; +} +class LocalSubscribeStoreTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + static DistributedKvDataManager manager; + static std::unique_ptr kvStorePtr; // declare kvstore instance. + static Status statusGetKvStore; + static AppId appId; + static StoreId storeId; + static int usleepTime; +}; +std::unique_ptr LocalSubscribeStoreTest::kvStorePtr = nullptr; +Status LocalSubscribeStoreTest::statusGetKvStore = Status::ERROR; +DistributedKvDataManager LocalSubscribeStoreTest::manager; +AppId LocalSubscribeStoreTest::appId; +StoreId LocalSubscribeStoreTest::storeId; + +void LocalSubscribeStoreTest::SetUpTestCase(void) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; // not supported yet. + options.autoSync = true; // not supported yet. + options.kvStoreType = KvStoreType::MULTI_VERSION; + + appId.appId = "odmf"; // define app name. + storeId.storeId = "student"; // define kvstore(database) name + manager.DeleteKvStore(appId, storeId); + // [create and] open and initialize kvstore instance. + manager.GetKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + kvStorePtr = std::move(kvStore); + }); +} + +void LocalSubscribeStoreTest::TearDownTestCase(void) +{} + +void LocalSubscribeStoreTest::SetUp(void) +{} + +void LocalSubscribeStoreTest::TearDown(void) +{} + +class KvStoreObserverUnitTest : public KvStoreObserver { +public: + std::vector insertEntries_; + std::vector updateEntries_; + std::vector deleteEntries_; + bool isClear_; + 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 ChangeNotification &changeNotification, std::unique_ptr snapshot); + + void OnChange(const ChangeNotification &changeNotification); + + // reset the callCount_ to zero. + void ResetToZero(); + + unsigned long GetCallCount() const; + +private: + unsigned long callCount_; +}; + +KvStoreObserverUnitTest::KvStoreObserverUnitTest() +{ + callCount_ = 0; + insertEntries_ = {}; + updateEntries_ = {}; + deleteEntries_ = {}; + isClear_ = false; +} + +void KvStoreObserverUnitTest::OnChange(const ChangeNotification &changeNotification, + std::unique_ptr snapshot) +{ + ZLOGD("begin."); + callCount_++; + const std::list insert = changeNotification.GetInsertEntries(); + insertEntries_.clear(); + for (const auto &entry : insert) { + insertEntries_.push_back(entry); + } + + const std::list update = changeNotification.GetUpdateEntries(); + updateEntries_.clear(); + for (const auto &entry : update) { + updateEntries_.push_back(entry); + } + + const std::list del = changeNotification.GetDeleteEntries(); + deleteEntries_.clear(); + for (const auto &entry : del) { + deleteEntries_.push_back(entry); + } + + changeNotification.GetDeviceId(); + isClear_ = changeNotification.IsClear(); +} + +void KvStoreObserverUnitTest::OnChange(const ChangeNotification &changeNotification) +{} + +void KvStoreObserverUnitTest::ResetToZero() +{ + callCount_ = 0; +} + +unsigned long KvStoreObserverUnitTest::GetCallCount() const +{ + return callCount_; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore001 +* @tc.desc: Subscribe success +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore001, TestSize.Level0) +{ + ZLOGI("KvStoreDdmSubscribeKvStore001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + observer = nullptr; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore002 +* @tc.desc: Subscribe fail, observer is null +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore002, TestSize.Level0) +{ + ZLOGI("KvStoreDdmSubscribeKvStore002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + std::shared_ptr observer = nullptr; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "SubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore003 +* @tc.desc: Subscribe success and OnChange callback after put +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore003, TestSize.Level0) +{ + ZLOGI("KvStoreDdmSubscribeKvStore003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + observer = nullptr; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore004 +* @tc.desc: The same observer subscribe three times and OnChange callback after put +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore004, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::STORE_ALREADY_SUBSCRIBE, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::STORE_ALREADY_SUBSCRIBE, status) << "SubscribeKvStore return wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore005 +* @tc.desc: The different observer subscribe three times and OnChange callback after put +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore005, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer1 = std::make_shared(); + observer1->ResetToZero(); + std::shared_ptr observer2 = std::make_shared(); + observer2->ResetToZero(); + std::shared_ptr observer3 = std::make_shared(); + observer3->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer1); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore failed, wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer2); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore failed, wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer3); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore failed, wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "Putting data to KvStore failed, wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer1->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer2->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer3->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer1); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer2); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer3); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore006 +* @tc.desc: Unsubscribe an observer and subscribe again - the map should be cleared after unsubscription. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore006, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore006 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + Key key3 = "Id3"; + Value value3 = "subscribe"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore007 +* @tc.desc: Subscribe to an observer - OnChange callback is called multiple times after the put operation. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore007, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore007 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + Key key3 = "Id3"; + Value value3 = "subscribe"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 3); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore008 +* @tc.desc: Subscribe to an observer - OnChange callback is called multiple times after the put&update operations. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore008, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore008 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + Key key3 = "Id1"; + Value value3 = "subscribe03"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 3); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore009 +* @tc.desc: Subscribe to an observer - OnChange callback is called multiple times after the putBatch operation. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore009, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore009 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + // before update. + std::vector entries1; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id4"; + entry4.value = "subscribe"; + entry5.key = "Id5"; + entry5.value = "subscribe"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(1000000); + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore010 +* @tc.desc: Subscribe to an observer - OnChange callback is called multiple times after the putBatch update operation. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore010, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore010 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + // before update. + std::vector entries1; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id1"; + entry4.value = "modify"; + entry5.key = "Id2"; + entry5.value = "modify"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore011 +* @tc.desc: Subscribe to an observer - OnChange callback is called after successful deletion. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore011, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore011 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Delete("Id1"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore012 +* @tc.desc: Subscribe to an observer - OnChange callback is not called after deletion of non-existing keys. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore012, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore012 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Delete("Id4"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore013 +* @tc.desc: Subscribe to an observer - OnChange callback is called after KvStore is cleared. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore013, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore013 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Clear(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Clear data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore014 +* @tc.desc: Subscribe to an observer - OnChange callback is not called after non-existing data in KvStore is cleared. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore014, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore014 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Clear(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Clear data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore015 +* @tc.desc: Subscribe to an observer - OnChange callback is called after the deleteBatch operation. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore015, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore015 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id1"); + keys.push_back("Id2"); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore016 +* @tc.desc: Subscribe to an observer - OnChange callback is called after deleteBatch of non-existing keys. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore016, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore016 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id4"); + keys.push_back("Id5"); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStore020 +* @tc.desc: Unsubscribe an observer two times. +* @tc.type: FUNC +* @tc.require: AR000CQDU9 AR000CQS37 +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStore020, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStore020 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::STORE_NOT_SUBSCRIBE, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification001 +* @tc.desc: Subscribe to an observer successfully - callback is called with a notification after the put operation. +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification001, TestSize.Level0) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification001 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + ZLOGD("kvstore_ddm_subscribekvstore_003"); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + ZLOGD("kvstore_ddm_subscribekvstore_003 size:%zu.", observer->insertEntries_.size()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification002 +* @tc.desc: Subscribe to the same observer three times - callback is called with a notification after the put operation. +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification002, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification002 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::STORE_ALREADY_SUBSCRIBE, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::STORE_ALREADY_SUBSCRIBE, status) << "SubscribeKvStore return wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification003 +* @tc.desc: The different observer subscribe three times and callback with notification after put +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification003, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification003 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer1 = std::make_shared(); + observer1->ResetToZero(); + std::shared_ptr observer2 = std::make_shared(); + observer2->ResetToZero(); + std::shared_ptr observer3 = std::make_shared(); + observer3->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer1); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer2); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer3); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key = "Id1"; + Value value = "subscribe"; + status = kvStorePtr->Put(key, value); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer1->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer1->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer1->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer1->insertEntries_[0].value.ToString()); + + EXPECT_EQ(static_cast(observer2->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer2->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer2->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer2->insertEntries_[0].value.ToString()); + + EXPECT_EQ(static_cast(observer3->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer3->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer3->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer3->insertEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer1); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer2); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer3); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification004 +* @tc.desc: Verify notification after an observer is unsubscribed and then subscribed again. +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification004, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification004 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + Key key3 = "Id3"; + Value value3 = "subscribe"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id3", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification005 +* @tc.desc: Subscribe to an observer, callback with notification many times after put the different data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification005, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification005 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id2", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + Key key3 = "Id3"; + Value value3 = "subscribe"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id3", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 3); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification006 +* @tc.desc: Subscribe to an observer, callback with notification many times after put the same data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification006, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification006 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + Key key2 = "Id1"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 1); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[0].value.ToString()); + + Key key3 = "Id1"; + Value value3 = "subscribe"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 1); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[0].value.ToString()); + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 3); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification007 +* @tc.desc: Subscribe to an observer, callback with notification many times after put&update +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification007, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification007 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + Key key1 = "Id1"; + Value value1 = "subscribe"; + Status status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + Key key2 = "Id2"; + Value value2 = "subscribe"; + status = kvStorePtr->Put(key2, value2); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key3 = "Id1"; + Value value3 = "subscribe03"; + status = kvStorePtr->Put(key3, value3); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 1); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe03", observer->updateEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification008 +* @tc.desc: Subscribe to an observer, callback with notification one times after putbatch&update +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification008, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification008 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::vector entries; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + entries.clear(); + entry1.key = "Id1"; + entry1.value = "subscribe_modify"; + entry2.key = "Id2"; + entry2.value = "subscribe_modify"; + entries.push_back(entry1); + entries.push_back(entry2); + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 2); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe_modify", observer->updateEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->updateEntries_[1].key.ToString()); + EXPECT_EQ("subscribe_modify", observer->updateEntries_[1].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification009 +* @tc.desc: Subscribe to an observer, callback with notification one times after putbatch all different data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification009, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification009 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 3); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + EXPECT_EQ("Id3", observer->insertEntries_[2].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[2].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification010 +* @tc.desc: Subscribe to an observer, callback with notification one times after putbatch both different and same data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification010, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification010 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id1"; + entry2.value = "subscribe"; + entry3.key = "Id2"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 2); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification011 +* @tc.desc: Subscribe to an observer, callback with notification one times after putbatch all same data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification011, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification011 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id1"; + entry2.value = "subscribe"; + entry3.key = "Id1"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification012 +* @tc.desc: Subscribe to an observer, callback with notification many times after putbatch all different data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification012, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification012 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries1; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id4"; + entry4.value = "subscribe"; + entry5.key = "Id5"; + entry5.value = "subscribe"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 3); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + EXPECT_EQ("Id3", observer->insertEntries_[2].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[2].value.ToString()); + + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 2); + EXPECT_EQ("Id4", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id5", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification013 +* @tc.desc: Subscribe to an observer, callback with notification many times after putbatch both different and same data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification013, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification013 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries1; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id1"; + entry4.value = "subscribe"; + entry5.key = "Id4"; + entry5.value = "subscribe"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 3); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + EXPECT_EQ("Id3", observer->insertEntries_[2].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[2].value.ToString()); + + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 1); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[0].value.ToString()); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id4", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification014 +* @tc.desc: Subscribe to an observer, callback with notification many times after putbatch all same data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification014, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification014 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries1; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id1"; + entry4.value = "subscribe"; + entry5.key = "Id2"; + entry5.value = "subscribe"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 3); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + EXPECT_EQ("Id3", observer->insertEntries_[2].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[2].value.ToString()); + + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 2); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->updateEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[1].value.ToString()); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification015 +* @tc.desc: Subscribe to an observer, callback with notification many times after putbatch complex data +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification015, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification015 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries1; + Entry entry1, entry2, entry3; + + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id1"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries1.push_back(entry1); + entries1.push_back(entry2); + entries1.push_back(entry3); + + std::vector entries2; + Entry entry4, entry5; + entry4.key = "Id1"; + entry4.value = "subscribe"; + entry5.key = "Id2"; + entry5.value = "subscribe"; + entries2.push_back(entry4); + entries2.push_back(entry5); + + status = kvStorePtr->PutBatch(entries1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 2); + EXPECT_EQ("Id1", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("Id3", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[1].value.ToString()); + + status = kvStorePtr->PutBatch(entries2); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 1); + EXPECT_EQ("Id1", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->updateEntries_[0].value.ToString()); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 1); + EXPECT_EQ("Id2", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->insertEntries_[0].value.ToString()); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 2); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification016 +* @tc.desc: Pressure test subscribe, callback with notification many times after putbatch +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification016, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification016 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + const int ENTRIES_MAX_LEN = 100; + std::vector entries; + for (int i = 0; i < ENTRIES_MAX_LEN; i++) { + Entry entry; + entry.key = std::to_string(i); + entry.value = "subscribe"; + entries.push_back(entry); + } + + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + usleep(USLEEP_TIME); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 100); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification017 +* @tc.desc: Subscribe to an observer, callback with notification after delete success +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification017, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification017 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Delete("Id1"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 1); + EXPECT_EQ("Id1", observer->deleteEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[0].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification018 +* @tc.desc: Subscribe to an observer, not callback after delete which key not exist +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification018, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification018 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Delete("Id4"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification019 +* @tc.desc: Subscribe to an observer, delete the same data many times and only first delete callback with notification +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification019, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification019 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + status = kvStorePtr->Delete("Id1"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 1); + EXPECT_EQ("Id1", observer->deleteEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[0].value.ToString()); + + status = kvStorePtr->Delete("Id1"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Delete data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 1); // not callback so not clear + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification020 +* @tc.desc: Subscribe to an observer, callback with notification after deleteBatch +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification020, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification020 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id1"); + keys.push_back("Id2"); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 2); + EXPECT_EQ("Id1", observer->deleteEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->deleteEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[1].value.ToString()); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification021 +* @tc.desc: Subscribe to an observer, not callback after deleteBatch which all keys not exist +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification021, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification021 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id4"); + keys.push_back("Id5"); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification022 +* @tc.desc: Subscribe to an observer, deletebatch the same data many times and only first deletebatch callback with +* notification +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification022, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification022 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id1"; + entry1.value = "subscribe"; + entry2.key = "Id2"; + entry2.value = "subscribe"; + entry3.key = "Id3"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id1"); + keys.push_back("Id2"); + + Status status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 2); + EXPECT_EQ("Id1", observer->deleteEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[0].value.ToString()); + EXPECT_EQ("Id2", observer->deleteEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[1].value.ToString()); + + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 2); // not callback so not clear + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification023 +* @tc.desc: Subscribe to an observer, include Clear Put PutBatch Delete DeleteBatch +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification023, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification023 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id2"; + entry1.value = "subscribe"; + entry2.key = "Id3"; + entry2.value = "subscribe"; + entry3.key = "Id4"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id2"); + keys.push_back("Id3"); + + kvStorePtr->Clear(); + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + status = kvStorePtr->Delete(key1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore delete data return wrong status"; + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 5); + // every callback will clear vector + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 2); + EXPECT_EQ("Id2", observer->deleteEntries_[0].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[0].value.ToString()); + EXPECT_EQ("Id3", observer->deleteEntries_[1].key.ToString()); + EXPECT_EQ("subscribe", observer->deleteEntries_[1].value.ToString()); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification024 +* @tc.desc: Subscribe to an observer[use transaction], include Clear Put PutBatch Delete DeleteBatch +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification024, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification024 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id2"; + entry1.value = "subscribe"; + entry2.key = "Id3"; + entry2.value = "subscribe"; + entry3.key = "Id4"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id2"); + keys.push_back("Id3"); + + status = kvStorePtr->StartTransaction(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore startTransaction return wrong status"; + kvStorePtr->Clear(); + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + status = kvStorePtr->Delete(key1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore delete data return wrong status"; + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + status = kvStorePtr->Commit(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Commit return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification025 +* @tc.desc: Subscribe to an observer[use transaction], include Clear Put PutBatch Delete DeleteBatch +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification025, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification025 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "Id1"; + Value value1 = "subscribe"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "Id2"; + entry1.value = "subscribe"; + entry2.key = "Id3"; + entry2.value = "subscribe"; + entry3.key = "Id4"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("Id2"); + keys.push_back("Id3"); + + status = kvStorePtr->StartTransaction(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore startTransaction return wrong status"; + kvStorePtr->Clear(); + status = kvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "KvStore put data return wrong status"; + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + status = kvStorePtr->Delete(key1); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore delete data return wrong status"; + status = kvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore DeleteBatch data return wrong status"; + status = kvStorePtr->Rollback(); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore Commit return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + observer = nullptr; +} + +/** +* @tc.name: KvStoreDdmSubscribeKvStoreNotification026 +* @tc.desc: Subscribe to an observer[use transaction], include bigData PutBatch update insert delete +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: dukaizhan +*/ +HWTEST_F(LocalSubscribeStoreTest, KvStoreDdmSubscribeKvStoreNotification026, TestSize.Level2) +{ + ZLOGI("KvStoreDdmSubscribeKvStoreNotification026 begin."); + EXPECT_EQ(Status::SUCCESS, statusGetKvStore) << "statusGetKvStore return wrong status"; + EXPECT_NE(nullptr, kvStorePtr) << "kvStorePtr is nullptr"; + kvStorePtr->Clear(); + + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = kvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + std::vector entries; + Entry entry0, entry1, entry2, entry3, entry4, entry5, entry6, entry7; + + int maxValueSize = 2 * 1024 * 1024; // max value size is 2M. + std::vector val(maxValueSize); + for (int i = 0; i < maxValueSize; i++) { + val[i] = static_cast(i); + } + Value value = val; + + int maxValueSize2 = 1000 * 1024; // max value size is 1000k. + std::vector val2(maxValueSize2); + for (int i = 0; i < maxValueSize2; i++) { + val2[i] = static_cast(i); + } + Value value2 = val2; + + entry0.key = "SingleKvStoreDdmPutBatch006_0"; + entry0.value = "beijing"; + entry1.key = "SingleKvStoreDdmPutBatch006_1"; + entry1.value = value; + entry2.key = "SingleKvStoreDdmPutBatch006_2"; + entry2.value = value; + entry3.key = "SingleKvStoreDdmPutBatch006_3"; + entry3.value = "ZuiHouBuZhiTianZaiShui"; + entry4.key = "SingleKvStoreDdmPutBatch006_4"; + entry4.value = value; + + entries.push_back(entry0); + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + entries.push_back(entry4); + + status = kvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + + usleep(USLEEP_TIME); + + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 5); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_0", observer->insertEntries_[0].key.ToString()); + EXPECT_EQ("beijing", observer->insertEntries_[0].value.ToString()); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_1", observer->insertEntries_[1].key.ToString()); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_2", observer->insertEntries_[2].key.ToString()); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_3", observer->insertEntries_[3].key.ToString()); + EXPECT_EQ("ZuiHouBuZhiTianZaiShui", observer->insertEntries_[3].value.ToString()); + + entry5.key = "SingleKvStoreDdmPutBatch006_2"; + entry5.value = val2; + entry6.key = "SingleKvStoreDdmPutBatch006_3"; + entry6.value = "ManChuanXingMengYaXingHe"; + entry7.key = "SingleKvStoreDdmPutBatch006_4"; + entry7.value = val2; + std::vector updateEntries; + updateEntries.push_back(entry5); + updateEntries.push_back(entry6); + updateEntries.push_back(entry7); + status = kvStorePtr->PutBatch(updateEntries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putBatch update data return wrong status"; + + usleep(USLEEP_TIME); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 3); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_2", observer->updateEntries_[0].key.ToString()); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_3", observer->updateEntries_[1].key.ToString()); + EXPECT_EQ("ManChuanXingMengYaXingHe", observer->updateEntries_[1].value.ToString()); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_4", observer->updateEntries_[2].key.ToString()); + EXPECT_EQ(false, observer->isClear_); + + status = kvStorePtr->Delete("SingleKvStoreDdmPutBatch006_3"); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore delete data return wrong status"; + usleep(1000000); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 1); + EXPECT_EQ("SingleKvStoreDdmPutBatch006_3", observer->deleteEntries_[0].key.ToString()); + + EXPECT_EQ(static_cast(observer->GetCallCount()), 3); + status = kvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: ChangeNotificationMarshalling001 +* @tc.desc: Test changenotification marshalling and unmarshalling function +* @tc.type: FUNC +* @tc.require: AR000CIFGM +* @tc.author: liuyuhui +*/ +HWTEST_F(LocalSubscribeStoreTest, ChangeNotificationMarshalling001, TestSize.Level0) +{ + ZLOGI("ChangeNotificationMarshalling001 begin."); + Entry insert, update, del; + insert.key = "insert"; + update.key = "update"; + del.key = "delete"; + insert.value = "insert_value"; + update.value = "update_value"; + del.value = "delete_value"; + std::list insertEntries, updateEntries, deleteEntries; + insertEntries.push_back(insert); + updateEntries.push_back(update); + deleteEntries.push_back(del); + + ChangeNotification changeIn(insertEntries, updateEntries, deleteEntries, std::string(), false); + OHOS::Parcel parcel; + changeIn.Marshalling(parcel); + ChangeNotification *changeOut = ChangeNotification::Unmarshalling(parcel); + ASSERT_NE(changeOut, nullptr); + ASSERT_EQ(changeOut->GetInsertEntries().size(), 1UL); + EXPECT_EQ(changeOut->GetInsertEntries().front().key.ToString(), std::string("insert")); + EXPECT_EQ(changeOut->GetInsertEntries().front().value.ToString(), std::string("insert_value")); + ASSERT_EQ(changeOut->GetUpdateEntries().size(), 1UL); + EXPECT_EQ(changeOut->GetUpdateEntries().front().key.ToString(), std::string("update")); + EXPECT_EQ(changeOut->GetUpdateEntries().front().value.ToString(), std::string("update_value")); + ASSERT_EQ(changeOut->GetDeleteEntries().size(), 1UL); + EXPECT_EQ(changeOut->GetDeleteEntries().front().key.ToString(), std::string("delete")); + EXPECT_EQ(changeOut->GetDeleteEntries().front().value.ToString(), std::string("delete_value")); + EXPECT_EQ(changeOut->IsClear(), false); + delete changeOut; +} + diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_query_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_query_test.cpp new file mode 100755 index 000000000..3f806991b --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_query_test.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SingleKvStoreClientQueryTest" + +#include +#include +#include +#include +#include +#include "distributed_kv_data_manager.h" +#include "types.h" +#include "log_print.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class SingleKvStoreClientQueryTest : public testing::Test { +public: + static void SetUpTestCase(void); + + static void TearDownTestCase(void); + + void SetUp(); + + void TearDown(); + + static std::unique_ptr singleKvStorePtr; + static Status statusGetKvStore; +}; + +const std::string VALID_SCHEMA_STRICT_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_SKIPSIZE\":0," + "\"SCHEMA_DEFINE\":{" + "\"name\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.name\"]}"; +std::unique_ptr SingleKvStoreClientQueryTest::singleKvStorePtr = nullptr; +Status SingleKvStoreClientQueryTest::statusGetKvStore = Status::ERROR; + +void SingleKvStoreClientQueryTest::SetUpTestCase(void) +{} + +void SingleKvStoreClientQueryTest::TearDownTestCase(void) +{} + +void SingleKvStoreClientQueryTest::SetUp(void) +{} + +void SingleKvStoreClientQueryTest::TearDown(void) +{} + +/** +* @tc.name: TestQueryC001 +* @tc.desc: Query reset. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC001, TestSize.Level0) +{ + ZLOGD("TestQueryC001 start"); + DataQuery query; + EXPECT_TRUE(query.ToString().length() == 0); + std::string str = "test value"; + query.EqualTo("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + EXPECT_TRUE(query.ToString().length() == 0); + ZLOGD("TestQueryC001 end"); +} + +/** +* @tc.name: TestQueryC002 +* @tc.desc: Query equalTo. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC002, TestSize.Level0) +{ + ZLOGD("TestQueryC002 start"); + DataQuery query; + query.EqualTo("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.EqualTo("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.EqualTo("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.EqualTo("$.test_field_name", false); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = ""; + query.EqualTo("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC002 end"); +} + +/** +* @tc.name: TestQueryC003 +* @tc.desc: Query notEqualTo. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC003, TestSize.Level0) +{ + ZLOGD("TestQueryC003 start"); + DataQuery query; + query.NotEqualTo("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.NotEqualTo("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.NotEqualTo("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.NotEqualTo("$.test_field_name", false); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = "test value"; + query.NotEqualTo("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC003 end"); +} + +/** +* @tc.name: TestQueryC004 +* @tc.desc: Query greaterThan. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC004, TestSize.Level0) +{ + ZLOGD("TestQueryC004 start"); + DataQuery query; + query.GreaterThan("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.GreaterThan("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.GreaterThan("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = "test value"; + query.GreaterThan("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC004 end"); +} + +/** +* @tc.name: TestQueryC005 +* @tc.desc: Query lessThan. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC005, TestSize.Level0) +{ + ZLOGD("TestQueryC005 start"); + DataQuery query; + query.LessThan("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.LessThan("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.LessThan("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = "test value"; + query.LessThan("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC005 end"); +} + +/** +* @tc.name: TestQueryC006 +* @tc.desc: Query greaterThanOrEqualTo. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC006, TestSize.Level0) +{ + ZLOGD("TestQueryC006 start"); + DataQuery query; + query.GreaterThanOrEqualTo("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.GreaterThanOrEqualTo("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.GreaterThanOrEqualTo("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = "test value"; + query.GreaterThanOrEqualTo("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC006 end"); +} + +/** +* @tc.name: TestQueryC007 +* @tc.desc: Query lessThanOrEqualTo. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC007, TestSize.Level0) +{ + ZLOGD("TestQueryC007 start"); + DataQuery query; + query.LessThanOrEqualTo("$.test_field_name", 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.LessThanOrEqualTo("$.test_field_name", (int64_t) 100); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + query.LessThanOrEqualTo("$.test_field_name", 1.23); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::string str = "test value"; + query.LessThanOrEqualTo("$.test_field_name", str); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC007 end"); +} + +/** +* @tc.name: TestQueryC008 +* @tc.desc: Query isNull. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC008, TestSize.Level0) +{ + ZLOGD("TestQueryC008 start"); + DataQuery query; + query.IsNull("$.test_field_name"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC008 end"); +} + +/** +* @tc.name: TestQueryC009 +* @tc.desc: Query in. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC009, TestSize.Level0) +{ + ZLOGD("TestQueryC009 start"); + DataQuery query; + std::vector vectInt{ 10, 20, 30 }; + query.InInt("$.test_field_name", vectInt); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectLong{ (int64_t) 100, (int64_t) 200, (int64_t) 300 }; + query.InLong("$.test_field_name", vectLong); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectDouble{ 1.23, 2.23, 3.23 }; + query.InDouble("$.test_field_name", vectDouble); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectString{ "value 1", "value 2", "value 3" }; + query.InString("$.test_field_name", vectString); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC009 end"); +} + +/** +* @tc.name: TestQueryC010 +* @tc.desc: Query in. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC010, TestSize.Level0) +{ + ZLOGD("TestQueryC010 start"); + DataQuery query; + std::vector vectInt{ 10, 20, 30 }; + query.NotInInt("$.test_field_name", vectInt); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectLong{ (int64_t) 100, (int64_t) 200, (int64_t) 300 }; + query.NotInLong("$.test_field_name", vectLong); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectDouble{ 1.23, 2.23, 3.23 }; + query.NotInDouble("$.test_field_name", vectDouble); + EXPECT_TRUE(query.ToString().length() > 0); + query.Reset(); + std::vector vectString{ "value 1", "value 2", "value 3" }; + query.NotInString("$.test_field_name", vectString); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC010 end"); +} + +/** +* @tc.name: TestQueryC011 +* @tc.desc: Query like. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC011, TestSize.Level0) +{ + ZLOGD("TestQueryC011 start"); + DataQuery query; + query.Like("$.test_field_name", "test value"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC011 end"); +} + +/** +* @tc.name: TestQueryC012 +* @tc.desc: Query unlike. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC012, TestSize.Level0) +{ + ZLOGD("TestQueryC012 start"); + DataQuery query; + query.Unlike("$.test_field_name", "test value"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC012 end"); +} + +/** +* @tc.name: TestQueryC013 +* @tc.desc: Query and. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC013, TestSize.Level0) +{ + ZLOGD("TestQueryC013 start"); + DataQuery query; + query.Like("$.test_field_name1", "test value1"); + query.And(); + query.Like("$.test_field_name2", "test value2"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC013 end"); +} + +/** +* @tc.name: TestQueryC014 +* @tc.desc: Query or. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC014, TestSize.Level0) +{ + ZLOGD("TestQueryC014 start"); + DataQuery query; + query.Like("$.test_field_name1", "test value1"); + query.Or(); + query.Like("$.test_field_name2", "test value2"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC014 end"); +} + +/** +* @tc.name: TestQueryC015 +* @tc.desc: Query orderBy. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC015, TestSize.Level0) +{ + ZLOGD("TestQueryC015 start"); + DataQuery query; + query.OrderByAsc("$.test_field_name1"); + query.Reset(); + query.OrderByDesc("$.test_field_name1"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC015 end"); +} + +/** +* @tc.name: TestQueryC016 +* @tc.desc: Query orderBy. +* @tc.type: FUNC +* @tc.require: AR000DPSF5 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC016, TestSize.Level0) +{ + ZLOGD("TestQueryC016 start"); + DataQuery query; + query.Limit(10, 100); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC016 end"); +} + +/** +* @tc.name: TestSingleKvStoreQueryC001 +* @tc.desc: query single KvStore. +* @tc.type: FUNC +* @tc.require: SR000DPCO9 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestSingleKvStoreQueryC001, TestSize.Level0) +{ + ZLOGD("TestSingleKvStoreQueryC001 start"); + + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = true, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION }; + options.schema = VALID_SCHEMA_STRICT_DEFINE; + AppId appId = { "SingleKvStoreClientQueryTestAppId1" }; + StoreId storeId = { "SingleKvStoreClientQueryTestStoreId1" }; + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + singleKvStorePtr = std::move(kvStore); + }); + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + singleKvStorePtr->Put("test_key_1", "{\"name\":1}"); + singleKvStorePtr->Put("test_key_2", "{\"name\":2}"); + singleKvStorePtr->Put("test_key_3", "{\"name\":3}"); + + DataQuery query; + query.NotEqualTo("$.name", 3); + std::vector results1; + Status status1 = singleKvStorePtr->GetEntriesWithQuery(query.ToString(), results1); + ASSERT_EQ(status1, Status::SUCCESS); + EXPECT_TRUE(results1.size() == 2); + std::vector results2; + Status status2 = singleKvStorePtr->GetEntriesWithQuery(query, results2); + ASSERT_EQ(status2, Status::SUCCESS); + EXPECT_TRUE(results2.size() == 2); + + std::unique_ptr callback1; + singleKvStorePtr->GetResultSetWithQuery(query.ToString(), [&](Status status3, std::unique_ptr call) { + ASSERT_EQ(status3, Status::SUCCESS); + callback1 = std::move(call); + EXPECT_TRUE(callback1->GetCount() == 2); + }); + auto closeResultSetStatus = singleKvStorePtr->CloseResultSet(std::move(callback1)); + ASSERT_EQ(closeResultSetStatus, Status::SUCCESS); + std::unique_ptr callback2; + singleKvStorePtr->GetResultSetWithQuery(query, [&](Status status4, std::unique_ptr call) { + ASSERT_EQ(status4, Status::SUCCESS); + callback2 = std::move(call); + EXPECT_TRUE(callback2->GetCount() == 2); + }); + closeResultSetStatus = singleKvStorePtr->CloseResultSet(std::move(callback2)); + ASSERT_EQ(closeResultSetStatus, Status::SUCCESS); + + int resultSize1; + Status status5 = singleKvStorePtr->GetCountWithQuery(query.ToString(), resultSize1); + ASSERT_EQ(status5, Status::SUCCESS); + EXPECT_TRUE(resultSize1 == 2); + int resultSize2; + Status status6 = singleKvStorePtr->GetCountWithQuery(query, resultSize2); + ASSERT_EQ(status6, Status::SUCCESS); + EXPECT_TRUE(resultSize2 == 2); + + singleKvStorePtr->Delete("test_key_1"); + singleKvStorePtr->Delete("test_key_2"); + singleKvStorePtr->Delete("test_key_3"); + Status status = manager.CloseAllKvStore(appId); + EXPECT_EQ(status, Status::SUCCESS); + status = manager.DeleteAllKvStore(appId); + EXPECT_EQ(status, Status::SUCCESS); + + ZLOGD("TestSingleKvStoreQueryC001 end"); +} + + +/** +* @tc.name: TestSingleKvStoreQueryC002 +* @tc.desc: query single KvStore. +* @tc.type: FUNC +* @tc.require: SR000DPCO9 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestSingleKvStoreQueryC002, TestSize.Level0) +{ + ZLOGD("TestSingleKvStoreQueryC002 start"); + + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = true, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION }; + options.schema = VALID_SCHEMA_STRICT_DEFINE; + AppId appId = { "SingleKvStoreClientQueryTestAppId2" }; + StoreId storeId = { "SingleKvStoreClientQueryTestStoreId2" }; + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + singleKvStorePtr = std::move(kvStore); + }); + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + singleKvStorePtr->Put("test_key_1", "{\"name\":1}"); + singleKvStorePtr->Put("test_key_2", "{\"name\":2}"); + singleKvStorePtr->Put("test_key_3", "{\"name\":3}"); + + DataQuery query; + query.NotEqualTo("$.name", 3); + query.And(); + query.EqualTo("$.name", 1); + std::vector results1; + Status status1 = singleKvStorePtr->GetEntriesWithQuery(query.ToString(), results1); + ASSERT_EQ(status1, Status::SUCCESS); + EXPECT_TRUE(results1.size() == 1); + std::vector results2; + Status status2 = singleKvStorePtr->GetEntriesWithQuery(query, results2); + ASSERT_EQ(status2, Status::SUCCESS); + EXPECT_TRUE(results2.size() == 1); + + std::unique_ptr callback1; + singleKvStorePtr->GetResultSetWithQuery(query.ToString(), [&](Status status3, std::unique_ptr call) { + ASSERT_EQ(status3, Status::SUCCESS); + callback1 = std::move(call); + EXPECT_TRUE(callback1->GetCount() == 1); + }); + auto closeResultSetStatus = singleKvStorePtr->CloseResultSet(std::move(callback1)); + ASSERT_EQ(closeResultSetStatus, Status::SUCCESS); + std::unique_ptr callback2; + singleKvStorePtr->GetResultSetWithQuery(query, [&](Status status4, std::unique_ptr call) { + ASSERT_EQ(status4, Status::SUCCESS); + callback2 = std::move(call); + EXPECT_TRUE(callback2->GetCount() == 1); + }); + closeResultSetStatus = singleKvStorePtr->CloseResultSet(std::move(callback2)); + ASSERT_EQ(closeResultSetStatus, Status::SUCCESS); + + int resultSize1; + Status status5 = singleKvStorePtr->GetCountWithQuery(query.ToString(), resultSize1); + ZLOGD("this is it %ul", status5); + ASSERT_EQ(status5, Status::SUCCESS); + EXPECT_TRUE(resultSize1 == 1); + int resultSize2; + Status status6 = singleKvStorePtr->GetCountWithQuery(query, resultSize2); + ASSERT_EQ(status6, Status::SUCCESS); + EXPECT_TRUE(resultSize2 == 1); + + singleKvStorePtr->Delete("test_key_1"); + singleKvStorePtr->Delete("test_key_2"); + singleKvStorePtr->Delete("test_key_3"); + Status status = manager.CloseAllKvStore(appId); + EXPECT_EQ(status, Status::SUCCESS); + status = manager.DeleteAllKvStore(appId); + EXPECT_EQ(status, Status::SUCCESS); + + ZLOGD("TestSingleKvStoreQueryC002 end"); +} + +/** +* @tc.name: TestQueryC017 +* @tc.desc: Query group prefix isNotNull. +* @tc.type: FUNC +* @tc.require: AR000EPAMV +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC017, TestSize.Level0) +{ + ZLOGD("TestQueryC017 start"); + DataQuery query; + query.KeyPrefix("prefix"); + query.BeginGroup(); + query.IsNotNull("$.name"); + query.EndGroup(); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC017 end"); +} + +/** +* @tc.name: TestQueryC018 +* @tc.desc: Query SetSuggestIndex. +* @tc.type: FUNC +* @tc.require: AR000F3PBJ +* @tc.author: liuwenhui +*/ +HWTEST_F(SingleKvStoreClientQueryTest, TestQueryC018, TestSize.Level0) +{ + ZLOGD("TestQueryC018 start"); + DataQuery query; + query.SetSuggestIndex("test_field_name"); + EXPECT_TRUE(query.ToString().length() > 0); + ZLOGD("TestQueryC018 end"); +} diff --git a/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_test.cpp b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_test.cpp new file mode 100755 index 000000000..a43cea10c --- /dev/null +++ b/frameworks/innerkitsimpl/distributeddatafwk/test/unittest/single_kvstore_client_test.cpp @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributed_kv_data_manager.h" +#include "types.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class SingleKvStoreClientTest : public testing::Test { +public: + static void SetUpTestCase(void); + + static void TearDownTestCase(void); + + void SetUp(); + + void TearDown(); + + static std::unique_ptr singleKvStorePtr; // declare kvstore instance. + static Status statusGetKvStore; + static int MAX_VALUE_SIZE; +}; + +const std::string VALID_SCHEMA_STRICT_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\"," + "\"SCHEMA_MODE\":\"STRICT\"," + "\"SCHEMA_SKIPSIZE\":0," + "\"SCHEMA_DEFINE\":{" + "\"age\":\"INTEGER, NOT NULL\"" + "}," + "\"SCHEMA_INDEXES\":[\"$.age\"]}"; + +std::unique_ptr SingleKvStoreClientTest::singleKvStorePtr = nullptr; +Status SingleKvStoreClientTest::statusGetKvStore = Status::ERROR; +int SingleKvStoreClientTest::MAX_VALUE_SIZE = 4 * 1024 * 1024; // max value size is 4M. + +void SingleKvStoreClientTest::SetUpTestCase(void) +{ + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = false, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION }; + + AppId appId = { "odmf" }; + StoreId storeId = { "student_single" }; // define kvstore(database) name. + + // [create and] open and initialize kvstore instance. + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + statusGetKvStore = status; + singleKvStorePtr = std::move(kvStore); + }); +} + +void SingleKvStoreClientTest::TearDownTestCase(void) +{} + +void SingleKvStoreClientTest::SetUp(void) +{} + +void SingleKvStoreClientTest::TearDown(void) +{} + +class KvStoreObserverTestImpl : public KvStoreObserver { +public: + std::vector insertEntries_; + std::vector updateEntries_; + std::vector deleteEntries_; + bool isClear_; + KvStoreObserverTestImpl(); + ~KvStoreObserverTestImpl() + {} + + KvStoreObserverTestImpl(const KvStoreObserverTestImpl &) = delete; + KvStoreObserverTestImpl &operator=(const KvStoreObserverTestImpl &) = delete; + KvStoreObserverTestImpl(KvStoreObserverTestImpl &&) = delete; + KvStoreObserverTestImpl &operator=(KvStoreObserverTestImpl &&) = delete; + + // callback function will be called when the db data is changed. + void OnChange(const ChangeNotification &changeNotification, std::unique_ptr snapshot); + + void OnChange(const ChangeNotification &changeNotification); + + // reset the callCount_ to zero. + void ResetToZero(); + + unsigned long GetCallCount() const; + +private: + unsigned long callCount_; +}; + +void KvStoreObserverTestImpl::OnChange(const ChangeNotification &changeNotification) +{ + callCount_++; + const std::list insert = changeNotification.GetInsertEntries(); + insertEntries_.clear(); + for (const auto &entry : insert) { + insertEntries_.push_back(entry); + } + + const std::list update = changeNotification.GetUpdateEntries(); + updateEntries_.clear(); + for (const auto &entry : update) { + updateEntries_.push_back(entry); + } + + const std::list del = changeNotification.GetDeleteEntries(); + deleteEntries_.clear(); + for (const auto &entry : del) { + deleteEntries_.push_back(entry); + } + + isClear_ = changeNotification.IsClear(); +} + +void KvStoreObserverTestImpl::OnChange(const ChangeNotification &changeNotification, + std::unique_ptr snapshot) +{} + + +KvStoreObserverTestImpl::KvStoreObserverTestImpl() +{ + callCount_ = 0; + insertEntries_ = {}; + updateEntries_ = {}; + deleteEntries_ = {}; + isClear_ = false; +} + +void KvStoreObserverTestImpl::ResetToZero() +{ + callCount_ = 0; +} + +unsigned long KvStoreObserverTestImpl::GetCallCount() const +{ + return callCount_; +} + +class KvStoreSyncCallbackTestImpl : public KvStoreSyncCallback { +public: + void SyncCompleted(const std::map &results); +}; + +void KvStoreSyncCallbackTestImpl::SyncCompleted(const std::map &results) +{} + +/** +* @tc.name: GetStoreId001 +* @tc.desc: Get a single KvStore instance. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, GetStoreId001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + + auto storID = singleKvStorePtr->GetStoreId(); + EXPECT_EQ(storID.storeId, "student_single"); +} + +/** +* @tc.name: PutGetDelete001 +* @tc.desc: put value and delete value +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, PutGetDelete001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + + Key skey = {"single_001"}; + Value sval = {"value_001"}; + auto status = singleKvStorePtr->Put(skey, sval); + EXPECT_EQ(status, Status::SUCCESS) << "putting data failed"; + + auto delStatus = singleKvStorePtr->Delete(skey); + EXPECT_EQ(delStatus, Status::SUCCESS) << "deleting data failed"; + + auto notExistStatus = singleKvStorePtr->Delete(skey); + EXPECT_EQ(notExistStatus, Status::SUCCESS) << "deleting non-existing data failed"; + + auto spaceStatus = singleKvStorePtr->Put(skey, {""}); + EXPECT_EQ(spaceStatus, Status::SUCCESS) << "putting space failed"; + + auto spaceKeyStatus = singleKvStorePtr->Put({""}, {""}); + EXPECT_NE(spaceKeyStatus, Status::SUCCESS) << "putting space keys failed"; + + Status validStatus = singleKvStorePtr->Put(skey, sval); + EXPECT_EQ(validStatus, Status::SUCCESS) << "putting valid keys and values failed"; + + Value rVal; + auto validPutStatus = singleKvStorePtr->Get(skey, rVal); + EXPECT_EQ(validPutStatus, Status::SUCCESS) << "Getting value failed"; + EXPECT_EQ(sval, rVal) << "Got and put values not equal"; +} + +/** +* @tc.name: GetEntriesAndResultSet001 +* @tc.desc: Batch put values and get values. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, GetEntriesAndResultSet001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + + // prepare 10 + unsigned long sum = 10; + int sum_1 = 10; + std::string prefix = "prefix_"; + for (unsigned long i = 0; i < sum; i++) { + singleKvStorePtr->Put({prefix + std::to_string(i)}, {std::to_string(i)}); + } + + std::vector results; + singleKvStorePtr->GetEntries({prefix}, results); + EXPECT_EQ(results.size(), sum) << "entries size is not equal 10."; + + std::unique_ptr callback; + singleKvStorePtr->GetResultSet({prefix}, [&](Status status, std::unique_ptr call) { + EXPECT_EQ(status, Status::SUCCESS); + callback = std::move(call); + }); + EXPECT_EQ(callback->GetCount(), sum_1) << "resultSet size is not equal 10."; + callback->IsFirst(); + callback->IsAfterLast(); + callback->IsBeforeFirst(); + callback->MoveToPosition(1); + callback->IsLast(); + callback->MoveToPrevious(); + callback->MoveToNext(); + callback->MoveToLast(); + callback->MoveToFirst(); + callback->GetPosition(); + Entry entry; + callback->GetEntry(entry); + + for (unsigned long i = 0; i < sum; i++) { + singleKvStorePtr->Delete({prefix + std::to_string(i)}); + } + + auto closeResultSetStatus = singleKvStorePtr->CloseResultSet(std::move(callback)); + EXPECT_EQ(closeResultSetStatus, Status::SUCCESS) << "close resultSet failed."; +} + +/** +* @tc.name: Subscribe001 +* @tc.desc: Put data and get callback. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, Subscribe001 ,TestSize.Level0) +{ + auto observer = std::make_shared(); + auto subStatus = singleKvStorePtr->SubscribeKvStore(SubscribeType::SUBSCRIBE_TYPE_ALL, observer); + EXPECT_EQ(subStatus, Status::SUCCESS) << "subscribe kvStore observer failed."; + // subscribe repeated observer; + auto repeatedSubStatus = singleKvStorePtr->SubscribeKvStore(SubscribeType::SUBSCRIBE_TYPE_ALL, observer); + EXPECT_NE(repeatedSubStatus, Status::SUCCESS) << "repeat subscribe kvStore observer failed."; + + auto unSubStatus = singleKvStorePtr->UnSubscribeKvStore(SubscribeType::SUBSCRIBE_TYPE_ALL, observer); + EXPECT_EQ(unSubStatus, Status::SUCCESS) << "unsubscribe kvStore observer failed."; +} + +/** +* @tc.name: SyncCallback001 +* @tc.desc: Register sync callback. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, SyncCallback001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + + auto syncCallback = std::make_shared(); + auto syncStatus = singleKvStorePtr->RegisterSyncCallback(syncCallback); + EXPECT_EQ(syncStatus, Status::SUCCESS) << "register sync callback failed."; + + auto unRegStatus = singleKvStorePtr->UnRegisterSyncCallback(); + EXPECT_EQ(unRegStatus, Status::SUCCESS) << "un register sync callback failed."; + + Key skey = {"single_001"}; + Value sval = {"value_001"}; + singleKvStorePtr->Put(skey, sval); + singleKvStorePtr->Delete(skey); + + std::map results; + results.insert({"aaa", Status::INVALID_ARGUMENT}); + syncCallback->SyncCompleted(results); +} + +/** +* @tc.name: RemoveDeviceData001 +* @tc.desc: Remove device data. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, RemoveDeviceData001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + + Key skey = {"single_001"}; + Value sval = {"value_001"}; + singleKvStorePtr->Put(skey, sval); + + std::string deviceId = "no_exist_device_id"; + auto removeStatus = singleKvStorePtr->RemoveDeviceData(deviceId); + EXPECT_NE(removeStatus, Status::SUCCESS) << "remove device should not return success"; + + Value retVal; + auto getRet = singleKvStorePtr->Get(skey, retVal); + EXPECT_EQ(getRet, Status::SUCCESS) << "get value failed."; + EXPECT_EQ(retVal.Size(), sval.Size()) << "data base should be null."; +} + +/** +* @tc.name: SyncData001 +* @tc.desc: Synchronize device data. +* @tc.type: FUNC +* @tc.require: SR000DORPS AR000DPRQ7 AR000DDPRPL +* @tc.author: hongbo +*/ +HWTEST_F(SingleKvStoreClientTest, SyncData001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + std::string deviceId = "no_exist_device_id"; + std::vector deviceIds = { deviceId }; + auto syncStatus = singleKvStorePtr->Sync(deviceIds, SyncMode::PUSH); + EXPECT_NE(syncStatus, Status::SUCCESS) << "sync device should not return success"; +} + +/** +* @tc.name: TestSchemaStoreC001 +* @tc.desc: Test schema single store. +* @tc.type: FUNC +* @tc.require: AR000DPSF1 +* @tc.author: YangLeda +*/ +HWTEST_F(SingleKvStoreClientTest, TestSchemaStoreC001 ,TestSize.Level0) +{ + std::unique_ptr schemaSingleKvStorePtr; + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = true, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION }; + options.schema = VALID_SCHEMA_STRICT_DEFINE; + AppId appId = { "schema_app_id" }; + StoreId storeId = { "schema_store_id" }; + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + schemaSingleKvStorePtr = std::move(kvStore); + }); + ASSERT_NE(schemaSingleKvStorePtr, nullptr) << "kvStorePtr is null."; + auto result = schemaSingleKvStorePtr->GetStoreId(); + EXPECT_EQ(result.storeId, "schema_store_id"); + + Key testKey = {"TestSchemaStoreC001_key"}; + Value testValue = {"{\"age\":10}"}; + auto testStatus = schemaSingleKvStorePtr->Put(testKey, testValue); + EXPECT_EQ(testStatus, Status::SUCCESS) << "putting data failed"; + Value resultValue; + auto getRet = schemaSingleKvStorePtr->Get(testKey, resultValue); + EXPECT_EQ(getRet, Status::SUCCESS) << "get value failed."; +} + +/** +* @tc.name: SyncData001 +* @tc.desc: Synchronize device data. +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(SingleKvStoreClientTest, SyncData002 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + std::string deviceId = "no_exist_device_id"; + std::vector deviceIds = { deviceId }; + uint32_t allowedDelayMs = 200; + auto syncStatus = singleKvStorePtr->Sync(deviceIds, SyncMode::PUSH, allowedDelayMs); + EXPECT_EQ(syncStatus, Status::SUCCESS) << "sync device should return success"; +} + +/** +* @tc.name: SyncData002 +* @tc.desc: Set sync parameters - success. +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAO +* @tc.author: wangtao +*/ +HWTEST_F(SingleKvStoreClientTest, SetSync001 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + KvSyncParam syncParam{ 500 }; // 500ms + auto ret = singleKvStorePtr->SetSyncParam(syncParam); + EXPECT_EQ(ret, Status::SUCCESS) << "set sync param should return success"; + + KvSyncParam syncParamRet; + singleKvStorePtr->GetSyncParam(syncParamRet); + EXPECT_EQ(syncParamRet.allowedDelayMs, syncParam.allowedDelayMs); +} + +/** +* @tc.name: SyncData002 +* @tc.desc: Set sync parameters - failed. +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAO +* @tc.author: wangtao +*/ +HWTEST_F(SingleKvStoreClientTest, SetSync002 ,TestSize.Level0) +{ + EXPECT_NE(singleKvStorePtr, nullptr) << "kvStorePtr is null."; + KvSyncParam syncParam2{ 50 }; // 50ms + auto ret = singleKvStorePtr->SetSyncParam(syncParam2); + EXPECT_NE(ret, Status::SUCCESS) << "set sync param should not return success"; + + KvSyncParam syncParamRet2; + ret = singleKvStorePtr->GetSyncParam(syncParamRet2); + EXPECT_NE(syncParamRet2.allowedDelayMs, syncParam2.allowedDelayMs); +} + +/** +* @tc.name: SingleKvStoreDdmPutBatch001 +* @tc.desc: Batch put data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch001, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "KvStoreDdmPutBatch001_1"; + entry1.value = "age:20"; + entry2.key = "KvStoreDdmPutBatch001_2"; + entry2.value = "age:19"; + entry3.key = "KvStoreDdmPutBatch001_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "KvStore putbatch data return wrong status"; + // get value from kvstore. + Value valueRet1; + Status statusRet1 = singleKvStorePtr->Get(entry1.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ(entry1.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = singleKvStorePtr->Get(entry2.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ(entry2.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = singleKvStorePtr->Get(entry3.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "KvStoreSnapshot get data return wrong status"; + EXPECT_EQ(entry3.value, valueRet3) << "value and valueRet are not equal"; +} + +/** +* @tc.name: SingleKvStoreDdmPutBatch002 +* @tc.desc: Batch update data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch002, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "SinglekvStorePtr is nullptr"; + + // before update. + std::vector entriesBefore; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmPutBatch002_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmPutBatch002_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmPutBatch002_3"; + entry3.value = "age:23"; + entriesBefore.push_back(entry1); + entriesBefore.push_back(entry2); + entriesBefore.push_back(entry3); + + Status status = singleKvStorePtr->PutBatch(entriesBefore); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore putbatch data return wrong status"; + + // after update. + std::vector entriesAfter; + Entry entry4, entry5, entry6; + entry4.key = "SingleKvStoreDdmPutBatch002_1"; + entry4.value = "age:20, sex:girl"; + entry5.key = "SingleKvStoreDdmPutBatch002_2"; + entry5.value = "age:19, sex:boy"; + entry6.key = "SingleKvStoreDdmPutBatch002_3"; + entry6.value = "age:23, sex:girl"; + entriesAfter.push_back(entry4); + entriesAfter.push_back(entry5); + entriesAfter.push_back(entry6); + + status = singleKvStorePtr->PutBatch(entriesAfter); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore putbatch failed, wrong status"; + + // get value from kvstore. + Value valueRet1; + Status statusRet1 = singleKvStorePtr->Get(entry4.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "SingleKvStore getting data failed, wrong status"; + EXPECT_EQ(entry4.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = singleKvStorePtr->Get(entry5.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "SingleKvStore getting data failed, wrong status"; + EXPECT_EQ(entry5.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = singleKvStorePtr->Get(entry6.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "SingleKvStore get data return wrong status"; + EXPECT_EQ(entry6.value, valueRet3) << "value and valueRet are not equal"; +} + +/** +* @tc.name: SingleKvStoreDdmPutBatch003 +* @tc.desc: Batch put data that contains invalid data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch003, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = " "; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = " "; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "singleKvStorePtr putbatch data return wrong status"; +} + +/** +* @tc.name: SingleKvStoreDdmPutBatch004 +* @tc.desc: Batch put data that contains invalid data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch004, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = ""; + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = ""; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "singleKvStorePtr putbatch data return wrong status"; +} + +std::string SingleGenerate1025KeyLen() +{ + std::string str("prefix"); + // Generate a key with a length of more than 1024 bytes. + for (int i = 0; i < 1024; i++) { + str += "a"; + } + return str; +} +/** +* @tc.name: SingleKvStoreDdmPutBatch005 +* @tc.desc: Batch put data that contains invalid data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch005, TestSize.Level2) +{ + + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = SingleGenerate1025KeyLen(); + entry1.value = "age:20"; + entry2.key = "student_name_caixu"; + entry2.value = "age:19"; + entry3.key = "student_name_liuyue"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + Status status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::INVALID_ARGUMENT, status) << "KvStore putbatch data return wrong status"; +} + +/** +* @tc.name: SingleKvStoreDdmPutBatch006 +* @tc.desc: Batch put large data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmPutBatch006, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + std::vector val(MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + val[i] = static_cast(i); + } + Value value = val; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmPutBatch006_1"; + entry1.value = value; + entry2.key = "SingleKvStoreDdmPutBatch006_2"; + entry2.value = value; + entry3.key = "SingleKvStoreDdmPutBatch006_3"; + entry3.value = value; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + Status status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "singleKvStorePtr putbatch data return wrong status"; + + // get value from kvstore. + Value valueRet1; + Status statusRet1 = singleKvStorePtr->Get(entry1.key, valueRet1); + EXPECT_EQ(Status::SUCCESS, statusRet1) << "singleKvStorePtr get data return wrong status"; + EXPECT_EQ(entry1.value, valueRet1) << "value and valueRet are not equal"; + + Value valueRet2; + Status statusRet2 = singleKvStorePtr->Get(entry2.key, valueRet2); + EXPECT_EQ(Status::SUCCESS, statusRet2) << "singleKvStorePtr get data return wrong status"; + EXPECT_EQ(entry2.value, valueRet2) << "value and valueRet are not equal"; + + Value valueRet3; + Status statusRet3 = singleKvStorePtr->Get(entry3.key, valueRet3); + EXPECT_EQ(Status::SUCCESS, statusRet3) << "singleKvStorePtr get data return wrong status"; + EXPECT_EQ(entry3.value, valueRet3) << "value and valueRet are not equal"; +} + +/** +* @tc.name: SingleKvStoreDdmDeleteBatch001 +* @tc.desc: Batch delete data. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmDeleteBatch001, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmDeleteBatch001_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmDeleteBatch001_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmDeleteBatch001_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreDdmDeleteBatch001_1"); + keys.push_back("SingleKvStoreDdmDeleteBatch001_2"); + keys.push_back("SingleKvStoreDdmDeleteBatch001_3"); + + Status status1 = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "singleKvStore putbatch data return wrong status"; + + Status status2 = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status2) << "singleKvStore deletebatch data return wrong status"; + std::vector results; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch001_", results); + unsigned long sum = 0; + EXPECT_EQ(results.size(), sum) << "entries size is not equal 0."; +} + +/** +* @tc.name: SingleKvStoreDdmDeleteBatch002 +* @tc.desc: Batch delete data when some keys are not in KvStore. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmDeleteBatch002, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmDeleteBatch002_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmDeleteBatch002_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmDeleteBatch002_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreDdmDeleteBatch002_1"); + keys.push_back("SingleKvStoreDdmDeleteBatch002_2"); + keys.push_back("SingleKvStoreDdmDeleteBatch002_3"); + keys.push_back("SingleKvStoreDdmDeleteBatch002_4"); + + Status status1 = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "KvStore putbatch data return wrong status"; + + Status status2 = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status2) << "KvStore deletebatch data return wrong status"; + std::vector results; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch002_", results); + unsigned long sum = 0; + EXPECT_EQ(results.size(), sum) << "entries size is not equal 0."; +} + +/** +* @tc.name: SingleKvStoreDdmDeleteBatch003 +* @tc.desc: Batch delete data when some keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmDeleteBatch003, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "SinglekvStorePtr is nullptr"; + + // Store entries to KvStore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmDeleteBatch003_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmDeleteBatch003_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmDeleteBatch003_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreDdmDeleteBatch003_1"); + keys.push_back("SingleKvStoreDdmDeleteBatch003_2"); + keys.push_back(""); + + Status status1 = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "SingleKvStore putbatch data return wrong status"; + + Status status2 = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "KvStore deletebatch data return wrong status"; + std::vector results; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch003_", results); + unsigned long sum = 3; + EXPECT_EQ(results.size(), sum) << "entries size is not equal 3."; +} + +/** +* @tc.name: SingleKvStoreDdmDeleteBatch004 +* @tc.desc: Batch delete data when some keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmDeleteBatch004, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmDeleteBatch004_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmDeleteBatch004_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmDeleteBatch004_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreDdmDeleteBatch004_1"); + keys.push_back("SingleKvStoreDdmDeleteBatch004_2"); + keys.push_back(" "); + + Status status1 = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "SingleKvStore putbatch data return wrong status"; + + std::vector results1; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch004_", results1); + unsigned long sum1 = 3; + EXPECT_EQ(results1.size(), sum1) << "entries size1111 is not equal 3."; + + Status status2 = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "SingleKvStore deletebatch data return wrong status"; + std::vector results; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch004_", results); + unsigned long sum = 3; + EXPECT_EQ(results.size(), sum) << "entries size is not equal 3."; +} + +/** +* @tc.name: SingleKvStoreDdmDeleteBatch005 +* @tc.desc: Batch delete data when some keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDdmDeleteBatch005, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + + // store entries to kvstore. + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreDdmDeleteBatch005_1"; + entry1.value = "age:20"; + entry2.key = "SingleKvStoreDdmDeleteBatch005_2"; + entry2.value = "age:19"; + entry3.key = "SingleKvStoreDdmDeleteBatch005_3"; + entry3.value = "age:23"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreDdmDeleteBatch005_1"); + keys.push_back("SingleKvStoreDdmDeleteBatch005_2"); + Key keyTmp = SingleGenerate1025KeyLen(); + keys.push_back(keyTmp); + + Status status1 = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status1) << "SingleKvStore putbatch data return wrong status"; + + std::vector results1; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch005_", results1); + unsigned long sum1 = 3; + EXPECT_EQ(results1.size(), sum1) << "entries111 size is not equal 3."; + + Status status2 = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::INVALID_ARGUMENT, status2) << "SingleKvStore deletebatch data return wrong status"; + std::vector results; + singleKvStorePtr->GetEntries("SingleKvStoreDdmDeleteBatch005_", results); + unsigned long sum = 3; + EXPECT_EQ(results.size(), sum) << "entries size is not equal 3."; +} + +/** +* @tc.name: SingleKvStoreTransaction001 +* @tc.desc: Batch delete data when some keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreTransaction001, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = singleKvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "SingleKvStoreTransaction001_1"; + Value value1 = "subscribe"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreTransaction001_2"; + entry1.value = "subscribe"; + entry2.key = "SingleKvStoreTransaction001_3"; + entry2.value = "subscribe"; + entry3.key = "SingleKvStoreTransaction001_4"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreTransaction001_2"); + keys.push_back("ISingleKvStoreTransaction001_3"); + + status = singleKvStorePtr->StartTransaction(); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore startTransaction return wrong status"; + + status = singleKvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore put data return wrong status"; + status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore putbatch data return wrong status"; + status = singleKvStorePtr->Delete(key1); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore delete data return wrong status"; + status = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore DeleteBatch data return wrong status"; + status = singleKvStorePtr->Commit(); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore Commit return wrong status"; + + usleep(200000); + EXPECT_EQ(static_cast(observer->GetCallCount()), 1); + + status = singleKvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; +} + +/** +* @tc.name: SingleKvStoreTransaction002 +* @tc.desc: Batch delete data when some keys are invalid. +* @tc.type: FUNC +* @tc.require: AR000DPSEA +* @tc.author: shanshuangshuang +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreTransaction002, TestSize.Level2) +{ + EXPECT_NE(nullptr, singleKvStorePtr) << "singleKvStorePtr is nullptr"; + std::shared_ptr observer = std::make_shared(); + observer->ResetToZero(); + + SubscribeType subscribeType = SubscribeType::SUBSCRIBE_TYPE_ALL; + Status status = singleKvStorePtr->SubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "SubscribeKvStore return wrong status"; + + Key key1 = "SingleKvStoreTransaction002_1"; + Value value1 = "subscribe"; + + std::vector entries; + Entry entry1, entry2, entry3; + entry1.key = "SingleKvStoreTransaction002_2"; + entry1.value = "subscribe"; + entry2.key = "SingleKvStoreTransaction002_3"; + entry2.value = "subscribe"; + entry3.key = "SingleKvStoreTransaction002_4"; + entry3.value = "subscribe"; + entries.push_back(entry1); + entries.push_back(entry2); + entries.push_back(entry3); + + std::vector keys; + keys.push_back("SingleKvStoreTransaction002_2"); + keys.push_back("SingleKvStoreTransaction002_3"); + + status = singleKvStorePtr->StartTransaction(); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore startTransaction return wrong status"; + + status = singleKvStorePtr->Put(key1, value1); // insert or update key-value + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore put data return wrong status"; + status = singleKvStorePtr->PutBatch(entries); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore putbatch data return wrong status"; + status = singleKvStorePtr->Delete(key1); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore delete data return wrong status"; + status = singleKvStorePtr->DeleteBatch(keys); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore DeleteBatch data return wrong status"; + status = singleKvStorePtr->Rollback(); + EXPECT_EQ(Status::SUCCESS, status) << "SingleKvStore Commit return wrong status"; + + usleep(200000); + EXPECT_EQ(static_cast(observer->GetCallCount()), 0); + EXPECT_EQ(static_cast(observer->insertEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->updateEntries_.size()), 0); + EXPECT_EQ(static_cast(observer->deleteEntries_.size()), 0); + + status = singleKvStorePtr->UnSubscribeKvStore(subscribeType, observer); + EXPECT_EQ(Status::SUCCESS, status) << "UnSubscribeKvStore return wrong status"; + observer = nullptr; +} + +/** +* @tc.name: SingleKvStoreDeviceSync001 +* @tc.desc: Test sync enable. +* @tc.type: FUNC +* @tc.require:AR000EPAM8 AR000EPAMD +* @tc.author: HongBo +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDeviceSync001 ,TestSize.Level0) +{ + std::unique_ptr schemaSingleKvStorePtr; + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = true, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION}; + AppId appId = { "schema_app_id001" }; + StoreId storeId = { "schema_store_id001" }; + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + schemaSingleKvStorePtr = std::move(kvStore); + }); + ASSERT_NE(schemaSingleKvStorePtr, nullptr) << "kvStorePtr is null."; + auto result = schemaSingleKvStorePtr->GetStoreId(); + EXPECT_EQ(result.storeId, "schema_store_id001"); + + auto testStatus = schemaSingleKvStorePtr->SetCapabilityEnabled(true); + EXPECT_EQ(testStatus, Status::SUCCESS) << "set fail"; +} + +/** +* @tc.name: SingleKvStoreDeviceSync002 +* @tc.desc: Test sync enable. +* @tc.type: FUNC +* @tc.require:SR000EPA22 AR000EPAM9 +* @tc.author: HongBo +*/ +HWTEST_F(SingleKvStoreClientTest, SingleKvStoreDeviceSync002 ,TestSize.Level0) +{ + std::unique_ptr schemaSingleKvStorePtr; + DistributedKvDataManager manager; + Options options = { .createIfMissing = true, .encrypt = true, .autoSync = true, + .kvStoreType = KvStoreType::SINGLE_VERSION}; + AppId appId = { "schema_app_id002" }; + StoreId storeId = { "schema_store_id002" }; + manager.GetSingleKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + schemaSingleKvStorePtr = std::move(kvStore); + }); + ASSERT_NE(schemaSingleKvStorePtr, nullptr) << "kvStorePtr is null."; + auto result = schemaSingleKvStorePtr->GetStoreId(); + EXPECT_EQ(result.storeId, "schema_store_id002"); + + std::vector local = {"A", "B"}; + std::vector remote = {"C", "D"}; + auto testStatus = schemaSingleKvStorePtr->SetCapabilityRange(local, remote); + EXPECT_EQ(testStatus, Status::SUCCESS) << "set range fail"; +} diff --git a/interfaces/innerkits/app_distributeddata/BUILD.gn b/interfaces/innerkits/app_distributeddata/BUILD.gn new file mode 100755 index 000000000..f2c659a4b --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/BUILD.gn @@ -0,0 +1,99 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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") + +group("build_module") { + deps = [ ":app_distributeddata" ] +} + +config("distributeddata_test_config") { + visibility = [ ":*" ] + + include_dirs = [ "include" ] +} + +config("distributeddatafwk_config") { + visibility = [ ":*" ] + + cflags = [ "-Wno-multichar" ] + + cflags_cc = [ "-fvisibility=hidden" ] + + include_dirs = [ + "include", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src", + "../../../frameworks/innerkitsimpl/distributeddatafwk/include", + "//utils/native/base/include", + ] +} + +config("distributeddatafwk_public_config") { + visibility = [ "//foundation/distributeddatamgr/distributeddatamgr:*" ] + + include_dirs = [ + "include", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/permission", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/account", + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/utils", + ] +} + +ohos_shared_library("app_distributeddata") { + part_name = "distributeddatamgr" + sources = [ + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_blob.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_change_notification.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_distributed_kv_data_manager_impl.h", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_conflict_data_impl.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_impl.h", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/app_kvstore_result_set_impl.cpp", + "include/app_types.h", + ] + + configs = [ ":distributeddatafwk_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//utils/native/base:utils", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + ] + + public_configs = [ ":distributeddatafwk_public_config" ] + + subsystem_name = "distributeddatamgr" +} + +config("distributeddatafwk_communication_config") { + visibility = [ ":*" ] + + cflags = [ "-Wno-multichar" ] + + include_dirs = [ + "include", + "//utils/native/base/include", + ] +} diff --git a/interfaces/innerkits/app_distributeddata/include/app_blob.h b/interfaces/innerkits/app_distributeddata/include/app_blob.h new file mode 100755 index 000000000..41b282251 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_blob.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 DISTRIBUTED_KV_APP_BLOB_H +#define DISTRIBUTED_KV_APP_BLOB_H + +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppBlob { +public: + KVSTORE_API AppBlob() = default; + KVSTORE_API ~AppBlob() = default; + + // Copy constructor for Blob. + KVSTORE_API AppBlob(const AppBlob &blob); + KVSTORE_API AppBlob &operator=(const AppBlob &blob); + + // Move constructor for Blob. + KVSTORE_API AppBlob(AppBlob &&blob) noexcept; + KVSTORE_API AppBlob &operator=(AppBlob &&blob) noexcept; + + // Construct a Blob using std::string. + KVSTORE_API AppBlob(const std::string &str); + + // Construct a Blob using char pointer and len. + KVSTORE_API AppBlob(const char *str, size_t n); + + // Construct a Blob using char pointer. + KVSTORE_API AppBlob(const char *str); + + // Construct a Blob using std::vector. + KVSTORE_API AppBlob(const std::vector &str); + + // Return a reference to the data of the blob. + KVSTORE_API const std::vector &Data() const; + + // Return the length (in bytes) of the referenced data. + KVSTORE_API size_t Size() const; + + // Return true if the length of the referenced data is zero. + KVSTORE_API bool Empty() const; + + KVSTORE_API bool operator==(const AppBlob &) const; + + // Change vector to std::string. + KVSTORE_API std::string ToString() const; + + // comparison. Returns value: + // < 0 if "*this" < "blob", + // == 0 if "*this" == "blob", + // > 0 if "*this" > "blob" + KVSTORE_API int Compare(const AppBlob &blob) const; + +private: + std::vector blob_; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // DISTRIBUTED_KV_APP_BLOB_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_change_notification.h b/interfaces/innerkits/app_distributeddata/include/app_change_notification.h new file mode 100644 index 000000000..f0f2d1ce2 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_change_notification.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 APP_CHANGE_NOTIFICATION_H +#define APP_CHANGE_NOTIFICATION_H + +#include +#include +#include "app_types.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppChangeNotification final { +public: + AppChangeNotification(); + + // constructor using changing data. + AppChangeNotification(const std::list &insertEntries, const std::list &updateEntries, + const std::list &deleteEntries, const std::string &deviceId, const bool isClear); + + KVSTORE_API ~AppChangeNotification(); + + // Function to get all inserted entries for this changing. + KVSTORE_API const std::list &GetInsertEntries() const; + + // Function to get all updated entries for this changing. + KVSTORE_API const std::list &GetUpdateEntries() const; + + // Function to get all deleted entries for this changing. + KVSTORE_API const std::list &GetDeleteEntries() const; + + // Function to get from device id. + KVSTORE_API const std::string &GetDeviceId() const; + + // Function to check if this change is made by calling clear function. + KVSTORE_API bool IsClear() const; + +private: + std::list insertEntries_; + + std::list updateEntries_; + + std::list deleteEntries_; + + std::string deviceId_; + + bool isClear_ = false; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_CHANGE_NOTIFICATION_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_device_status_change_listener.h b/interfaces/innerkits/app_distributeddata/include/app_device_status_change_listener.h new file mode 100755 index 000000000..83e65db77 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_device_status_change_listener.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 APP_DEVICE_STATUS_CHANGE_LISTENER_H +#define APP_DEVICE_STATUS_CHANGE_LISTENER_H + +#include "app_types.h" + +namespace OHOS { +namespace AppDistributedKv { +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 AppDistributedKv +} // namespace OHOS + +#endif // APP_DEVICE_STATUS_CHANGE_LISTENER_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_distributed_kv_data_manager.h b/interfaces/innerkits/app_distributeddata/include/app_distributed_kv_data_manager.h new file mode 100755 index 000000000..b6abf9c81 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_distributed_kv_data_manager.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_KV_DATA_MANAGER_H +#define APP_DISTRIBUTED_KV_DATA_MANAGER_H + +#include +#include "app_device_status_change_listener.h" +#include "app_kvstore.h" +#include "app_types.h" +#include "app_kvstore_corruption_observer.h" + +namespace OHOS { +namespace AppDistributedKv { +// This is the overall manager of all kvstore. +// This class provides open, close, delete AppKvStore and manage remote device functions. +class AppDistributedKvDataManager { +public: + // Get AppDistributedKvDataManager singleton for APP process (for jni and dynamic library depended by jni). + // Parameters: + // bundleName: bundleName of your app + // dataDir: the directory to save your db file. Please choose a directory you can visit before phone unlock. + // this parameter will not be checked or used after first successful call. + // userId: name of your user. this stands for multiuser, not for huawei account or linux user. + // Return: + // singleton of AppDistributedKvDataManager, or nullptr on error. + KVSTORE_API static std::shared_ptr GetInstance(const std::string &bundleName, + const std::string &dataDir, + const std::string &userId = "account0"); + + KVSTORE_API AppDistributedKvDataManager() + {} + + KVSTORE_API virtual ~AppDistributedKvDataManager() + {} + + // Open kvstore instance with the given storeId, creating it if needed. + // It is allowed to open the same kvstore concurrently + // multiple times, but only one database instance will be created. + // Parameters: + // options: the config of the kvstore, including encrypt, create if needed and whether need sync between + // devices. + // storeId: the name of the kvstore. + // callback: KvStore instance returned by this call. + // callback will return: + // if Options.createIfMissing is false and kvstore has not been created before, nullptr and Status is + // STORE_NOT_FOUND, + // if storeId is not valid, nullptr and Status is INVALID_ARGUMENT + // otherwise, SUCCESS and the unipue_ptr of AppKvStore, which client can use to operate kvstore, will be + // returned. + // Return: + // Status of this get operation. + KVSTORE_API + virtual Status GetKvStore(const Options &options, const std::string &storeId, + const std::function appKvStore)> &callback) = 0; + + // WARNING: try to close a KvStore while other thread(s) still using it may cause process crash. + // Disconnect kvstore connection from database instance with the given storeId, + // only if all connections to the same database instance are closed, database instance will be freed. + // after this call, kvstore becomes invalid. + // call to it will return nullptr exception. + // Parameters: + // appKvStore: kvstore instance created by GetKvStore. + // Return: + // Status of this close operation. + KVSTORE_API virtual Status CloseKvStore(std::unique_ptr appKvStore) = 0; + + // Delete database file with the given storeId. + // Client should first close all connections to it and then delete it, otherwise delete will return error. + // Parameters: + // storeId: the name of the kvstore. + // Return: + // Status of this delete operation. + KVSTORE_API virtual Status DeleteKvStore(const std::string &storeId) = 0; + + // Get the database size. + KVSTORE_API virtual Status GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) = 0; + + // observe + // Parameters: + // observer: observer which will be callback when corrupted. + // Return: + // Status of this operation. + KVSTORE_API + virtual Status RegisterKvStoreCorruptionObserver(const std::shared_ptr observer) = 0; +protected: + AppDistributedKvDataManager(const AppDistributedKvDataManager &) = delete; + AppDistributedKvDataManager& operator=(const AppDistributedKvDataManager&) = delete; + AppDistributedKvDataManager(AppDistributedKvDataManager &&) = delete; + AppDistributedKvDataManager& operator=(AppDistributedKvDataManager &&) = delete; +}; // class AppDistributedKvDataManager +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_DISTRIBUTED_KV_DATA_MANAGER_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_kvstore.h b/interfaces/innerkits/app_distributeddata/include/app_kvstore.h new file mode 100755 index 000000000..ddc52c257 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_kvstore.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_KV_STORE_H +#define APP_KV_STORE_H + +#include +#include +#include "app_kvstore_conflict_data.h" +#include "app_kvstore_observer.h" +#include "app_types.h" +#include "app_kvstore_result_set.h" + +namespace OHOS { +namespace AppDistributedKv { +// This is a public interface. Implementation of this class is in AppKvStoreImpl. +// This class provides put, delete, search, sync and subscribe functions of a key-value store. +class AppKvStore { +public: + KVSTORE_API virtual ~AppKvStore() + {} + + // Get id of this AppKvStore. + KVSTORE_API virtual const std::string &GetStoreId() = 0; + + // Write a pair of key and value to this store. Set write option to local if you do not this entry sync to other + // devices. + // Parameters: + // options: mark this is a local entry or not. + // key: key of this entry. Should be less than 256 bytes. key will be trimmed before store. + // value: value of this entry. Should be less than (1024 * 1024) bytes. + // Return: + // Status of this put operation. + KVSTORE_API virtual Status Put(const WriteOptions &options, const Key &key, const Value &value) = 0; + + // Delete an entry by its key. Set write option to local if you want this delete to be a local change. + // Parameters: + // options: mark this delete is a local change or not. + // key: key of the entry to be deleted. + // Return: + // Status of this delete operation. + KVSTORE_API virtual Status Delete(const WriteOptions &options, const Key &key) = 0; + + // Get value from AppKvStore by its key. Set options->local to true if you want to get from local kvstore. + // Parameters: + // options: mark we get from local store or remote store. options->batch is a reserved parameter and should + // always be false. + // key: key of this entry. + // value: value will be returned in this parameter. + // Return: + // Status of this get operation. + KVSTORE_API virtual Status Get(const ReadOptions &options, const Key &key, Value &value) = 0; + + // Get all entries in this store which key start with prefixKey. This function will always get from synced store. + // Parameters: + // prefixkey: the prefix to be searched. + // entries: entries will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + KVSTORE_API virtual Status GetEntries(const Key &prefixKey, std::vector &entries) = 0; + + // Get all entries in this store which key start with prefixKey. This function will always get from synced store. + // Parameters: + // prefixkey: the prefix to be searched. + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + KVSTORE_API virtual Status GetEntries(const Key &prefixKey, AppKvStoreResultSet *&resultSet) = 0; + + // Close the result set returned by GetEntries(). + // Parameters: + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + KVSTORE_API virtual Status CloseResultSet(AppKvStoreResultSet *&resultSet) = 0; + + // Sync store with other devices. This is an asynchronous method, + // sync will fail if there is a syncing operation in progress. + // Parameters: + // deviceIdList: device list to sync. + // mode: mode can be set to SyncMode::PUSH, SyncMode::PULL and SyncMode::PUTH_PULL. PUSH_PULL will firstly + // push all not-local store to listed devices, then pull these stores back. + // callback: return map to caller. + // Return: + // Status of this Sync operation. + KVSTORE_API virtual Status Sync(const std::vector &deviceIdList, const SyncMode &mode, + const std::function &)> &callback) = 0; + + // Register change of this kvstore to a client-defined observer. observer->OnChange method will be called when store + // changes. One observer can subscribe more than one AppKvStore. + // Parameters: + // options: mark this is a local entry or not. + // subscribeType: OBSERVER_CHANGES_NATIVE means native changes of syncable kv store, + // : OBSERVER_CHANGES_FOREIGN means synced data changes from remote devices, + // : OBSERVER_CHANGES_ALL means both native changes and synced data changes. + // observer: observer to subscribe changes. + // Return: + // Status of this subscribe operation. + KVSTORE_API virtual Status SubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) = 0; + + // Unregister a kvstore to an observer. + // Parameters: + // options: mark this is a local entry or not. + // subscribeType: reserved parameter. Current is always SubscribeType::DEFAULT. + // observer: observer to unsubscribe this store. + // Return: + // Status of this unsubscribe operation. + KVSTORE_API virtual Status UnSubscribeKvStore(const ReadOptions &options, const SubscribeType &subscribeType, + AppKvStoreObserver *observer) = 0; + + // Remove the device data synced from remote. + // Parameters: + // device: device id. + // Return: + // Status of this remove operation. + KVSTORE_API virtual Status RemoveDeviceData(const std::string &device) = 0; + + // Set policy of conflict resolution. + // Parameters: + // conflictType: include CONFLICT_FOREIGN_KEY_ONLY CONFLICT_FOREIGN_KEY_ORIG CONFLICT_NATIVE_ALL. + // callback: conflict resolution callback. + // Return: + // Status of Setting policy operation. + KVSTORE_API + virtual Status SetConflictResolutionPolicy(AppKvStoreConflictPolicyType conflictType, + std::function callback) = 0; + + // Export current data store to ${filePath} using ${passwd} + // Parameters: + // filePath: directory which store will be saved. + // passwd: can be null, which means dont cipher. + // Return: + // Status of this operation. + KVSTORE_API virtual Status Export(const std::string &filePath, const std::vector &passwd) = 0; + + // Import current data store to ${filePath} using ${passwd} + // Parameters: + // filePath: directory from which will recovery. + // passwd: can be null, which means dont cipher. + // Return: + // Status of this operation. + KVSTORE_API virtual Status Import(const std::string &filePath, const std::vector &passwd) = 0; + + // get security level. + // Parameters: + // securityLevel: the security level. + // Return: + // Status of this operation. + KVSTORE_API virtual Status GetSecurityLevel(SecurityLevel &securityLevel) const = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_KV_STORE_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_kvstore_conflict_data.h b/interfaces/innerkits/app_distributeddata/include/app_kvstore_conflict_data.h new file mode 100644 index 000000000..3674e9d93 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_kvstore_conflict_data.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 APP_KVSTOR_CONFLICT_DATA_H +#define APP_KVSTOR_CONFLICT_DATA_H + +#include +#include +#include "app_types.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppKvStoreConflictData { +public: + enum class ConflictValueType { + OLD_VALUE = 0, + NEW_VALUE, + }; + + KVSTORE_API virtual ~AppKvStoreConflictData() = default; + + KVSTORE_API virtual AppKvStoreConflictPolicyType GetType() const = 0; + + KVSTORE_API virtual void GetKey(Key &key) const = 0; + + KVSTORE_API virtual Status GetValue(ConflictValueType type, Value &value) const = 0; + + KVSTORE_API virtual bool IsDeleted(ConflictValueType type) const = 0; + + KVSTORE_API virtual bool IsNative(ConflictValueType type) const = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_KVSTOR_CONFLICT_DATA_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_kvstore_corruption_observer.h b/interfaces/innerkits/app_distributeddata/include/app_kvstore_corruption_observer.h new file mode 100644 index 000000000..a0d945a35 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_kvstore_corruption_observer.h @@ -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 DISTRIBUTEDDATAMGR_APP_KVSTORE_CORRUPTION_OBSERVER_H +#define DISTRIBUTEDDATAMGR_APP_KVSTORE_CORRUPTION_OBSERVER_H +namespace OHOS { +namespace AppDistributedKv { +class AppKvStoreCorruptionObserver { +public: + KVSTORE_API virtual ~AppKvStoreCorruptionObserver() {}; + KVSTORE_API + virtual void OnCorruption(const std::string &appId, const std::string &userId, const std::string &storeId) = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_APP_KVSTORE_CORRUPTION_OBSERVER_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_kvstore_observer.h b/interfaces/innerkits/app_distributeddata/include/app_kvstore_observer.h new file mode 100644 index 000000000..b536e5056 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_kvstore_observer.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 APP_KVSTORE_OBSERVER_H +#define APP_KVSTORE_OBSERVER_H + +#include "app_change_notification.h" + +namespace OHOS { +namespace AppDistributedKv { +// This is a abstract classes. Client needs to implement this class by self. +class AppKvStoreObserver { +public: + KVSTORE_API AppKvStoreObserver() = default; + + KVSTORE_API virtual ~AppKvStoreObserver() + {} + // This virtual function will be called on store change. + // Client needs to override this function to receive change notification. + // Parameters: + // ChangeNotification: all changes from other devices. + KVSTORE_API virtual void OnChange(const AppChangeNotification &appChangeNotification) = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_KV_STORE_OBSERVER_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_kvstore_result_set.h b/interfaces/innerkits/app_distributeddata/include/app_kvstore_result_set.h new file mode 100755 index 000000000..0e8085b48 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_kvstore_result_set.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 APP_KV_STORE_RESULT_SET_H +#define APP_KV_STORE_RESULT_SET_H + +#include "app_types.h" + +namespace OHOS { +namespace AppDistributedKv { +class AppKvStoreResultSet { +public: + KVSTORE_API virtual ~AppKvStoreResultSet() + {} + + // Returns the count of rows in the result set. + KVSTORE_API virtual int GetCount() const = 0; + + // Returns the current read position of the result set. + KVSTORE_API virtual int GetPosition() const = 0; + + // Move the read position to the first row, return false if the result set is empty. + KVSTORE_API virtual bool MoveToFirst() = 0; + + // Move the read position to the last row, return false if the result set is empty. + KVSTORE_API virtual bool MoveToLast() = 0; + + // Move the read position to the next row, + // and returns false if the result set is empty or the read position is already + // past the last entry in the result set. + KVSTORE_API virtual bool MoveToNext() = 0; + + // Move the read position to the previous row, + // and returns false if result set is empty or the read position is already + // before the first entry in the result set. + KVSTORE_API virtual bool MoveToPrevious() = 0; + + // Move the read position by a relative amount from the current position. + KVSTORE_API virtual bool Move(int offset) = 0; + + // Move the read position to an absolute position value. + KVSTORE_API virtual bool MoveToPosition(int position) = 0; + + // Returns whether the read position is pointing to the first row. + KVSTORE_API virtual bool IsFirst() const = 0; + + // Returns whether the read position is pointing to the last row. + KVSTORE_API virtual bool IsLast() const = 0; + + // Returns whether the read position is before the first row. + KVSTORE_API virtual bool IsBeforeFirst() const = 0; + + // Returns whether the read position is after the last row. + KVSTORE_API virtual bool IsAfterLast() const = 0; + + // Get a key-value entry. + KVSTORE_API virtual Status GetEntry(Entry &entry) const = 0; + + KVSTORE_API virtual Status Close() = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_KV_STORE_RESULT_SET_H diff --git a/interfaces/innerkits/app_distributeddata/include/app_types.h b/interfaces/innerkits/app_distributeddata/include/app_types.h new file mode 100755 index 000000000..c36bd26f0 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/app_types.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "app_blob.h" +#include "visibility.h" + +namespace OHOS { +namespace AppDistributedKv { +// Key set by the client, which can be any non-empty byte array with a length of less than 256 bytes. +using Key = OHOS::AppDistributedKv::AppBlob; + +// Value set by client, which can be any byte array. +using Value = OHOS::AppDistributedKv::AppBlob; + +// User ID from the user account +struct UserId { + std::string userId; +}; + +// App ID from the BMS +struct AppId { + std::string appId; +}; + +struct PipeInfo { + std::string pipeId; + std::string userId; +}; + +struct DeviceInfo { + std::string deviceId; + std::string deviceName; + std::string deviceType; +}; + +enum class MessageType { + DEFAULT = 0, + FILE = 1, +}; + +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, + STORE_NOT_OPEN = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 3, + STORE_NOT_FOUND = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 4, + STORE_ALREADY_SUBSCRIBE = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 5, + STORE_NOT_SUBSCRIBE = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 6, + KEY_NOT_FOUND = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 7, + DB_ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 8, + DEVICE_NOT_FOUND = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 9, + NETWORK_ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 10, + NO_DEVICE_CONNECTED = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 11, + PERMISSION_DENIED = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 12, + TIME_OUT = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 13, + REPEATED_REGISTER = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 14, + CREATE_SESSION_ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 15, + SECURITY_LEVEL_ERROR = APP_DISTRIBUTEDDATAMGR_ERR_OFFSET + 32, +}; + +enum class SubscribeType { + DEFAULT = 0, // reserved value, to be deleted + OBSERVER_CHANGES_NATIVE = 1, // native changes of syncable KvStore + OBSERVER_CHANGES_FOREIGN = 2, // synced data changes from remote devices + OBSERVER_CHANGES_ALL = 3, // both native changes and synced data changes +}; + +struct Entry { + Key key; + Value value; +}; + +enum class SyncMode { + PULL, + PUSH, + PUSH_PULL, +}; + +enum ConflictResolvePolicy { + LAST_WIN = 0, + DEVICE_COLLABORATION, +}; + +enum SecurityLevel : int { + NO_LABEL, + S0, + S1, + S2, + S3_EX, + S3, + S4, +}; + +struct Options { + bool createIfMissing = false; + bool encrypt = false; + bool persistant = false; + int conflictResolvePolicy = LAST_WIN; + int securityLevel = SecurityLevel::NO_LABEL; +}; + +struct WriteOptions { + bool local; +}; + +struct ReadOptions { + bool local; +}; + +template +std::vector TransferTypeToByteArray(const T &t) +{ + return std::vector(reinterpret_cast(const_cast(&t)), + reinterpret_cast(const_cast(&t)) + sizeof(T)); +} + +template +T TransferByteArrayToType(const std::vector &blob) +{ + // replace assert to HILOG_FATAL when HILOG_FATAL is ok. + if (blob.size() != sizeof(T) || blob.size() == 0) { + constexpr int tSize = sizeof(T); + uint8_t tContent[tSize] = { 0 }; + return *reinterpret_cast(tContent); + } + return *reinterpret_cast(const_cast(&blob[0])); +} + +enum class AppKvStoreConflictPolicyType { + CONFIICT_FOREIGN_KEY_ONLY = 0x01, + CONFLICT_FOREIGN_KEY_ORIG = 0x02, + CONFLICT_NATIVE_ALL = 0x0c, +}; +} // namespace AppDistributedKv +} // namespace OHOS + +#endif // APP_DISTRIBUTED_KVSTORE_TYPES_H diff --git a/interfaces/innerkits/app_distributeddata/include/visibility.h b/interfaces/innerkits/app_distributeddata/include/visibility.h new file mode 100644 index 000000000..2ffb65244 --- /dev/null +++ b/interfaces/innerkits/app_distributeddata/include/visibility.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/interfaces/innerkits/distributeddata/BUILD.gn b/interfaces/innerkits/distributeddata/BUILD.gn new file mode 100755 index 000000000..ca71e27dd --- /dev/null +++ b/interfaces/innerkits/distributeddata/BUILD.gn @@ -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. +import("//build/ohos.gni") + +group("build_module") { + deps = [ ":distributeddata" ] +} + +config("distributeddatafwk_config") { + visibility = [ ":*" ] + + cflags = [ "-Wno-multichar" ] + + include_dirs = [ + "include", + "../../../frameworks/innerkitsimpl/distributeddatafwk/include", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src", + "//utils/system/safwk/native/include", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + ] +} + +config("distributeddatafwk_public_config") { + visibility = [ "//foundation/distributeddatamgr/distributeddatamgr:*" ] + + include_dirs = [ + "include", + "//utils/native/base/include", + ] +} + +ohos_shared_library("distributeddata") { + part_name = "distributeddatamgr" + sources = [ + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/blob.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/change_notification.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/data_query.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/device_status_change_listener_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/distributed_kv_data_manager.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/idevice_status_change_listener_impl.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_client_death_observer.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_data_service.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_observer.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_resultset.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_single.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_snapshot.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/ikvstore_sync_callback.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_client_death_observer.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_death_recipient_impl.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_observer_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_resultset_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_service_death_notifier.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_snapshot_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/kvstore_sync_callback_client.cpp", + "../../../frameworks/innerkitsimpl/distributeddatafwk/src/single_kvstore_client.cpp", + "include/types.h", + ] + + configs = [ ":distributeddatafwk_config" ] + + deps = [ + "//utils/native/base:utils", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + ] + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] + + public_configs = [ ":distributeddatafwk_public_config" ] + + subsystem_name = "distributeddatamgr" +} diff --git a/interfaces/innerkits/distributeddata/include/blob.h b/interfaces/innerkits/distributeddata/include/blob.h new file mode 100644 index 000000000..da9b9c2bf --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/blob.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 DISTRIBUTED_KV_BLOB_H +#define DISTRIBUTED_KV_BLOB_H + +#include +#include +#include "parcel.h" +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +// note: Blob derives from Parcelable, so hiding inner a interface using blob is not possible unless Parcelable +// declared its interface as visible. +class Blob : public virtual Parcelable { +public: + KVSTORE_API Blob(); + + KVSTORE_API ~Blob() = default; + + // copy constructor for Blob. + KVSTORE_API Blob(const Blob &blob); + KVSTORE_API Blob &operator=(const Blob &blob); + + // move constructor for Blob. + KVSTORE_API Blob(Blob &&blob); + KVSTORE_API Blob &operator=(Blob &&blob); + + // construct a Blob use std::string. + KVSTORE_API Blob(const std::string &str); + + // construct a Blob use char pointer and len. + KVSTORE_API Blob(const char *str, size_t n); + + // construct a Blob use char pointer. + KVSTORE_API Blob(const char *str); + + // construct a Blob use std::vector + KVSTORE_API Blob(const std::vector &bytes); + + // construct a Blob use std::vector + KVSTORE_API Blob(std::vector &&bytes); + + // Return a reference to the data of the blob. + KVSTORE_API const std::vector &Data() const; + + // Return the length (in bytes) of the referenced data + KVSTORE_API size_t Size() const; + + // Return the occupied length when write this blob to rawdata + int RawSize() const; + + // Return true if the length of the referenced data is zero + KVSTORE_API bool Empty() const; + + // Return the the byte in the referenced data. + // REQUIRES: n < size() + KVSTORE_API uint8_t operator[](size_t n) const; + + KVSTORE_API bool operator==(const Blob &) const; + + // Change this blob to refer to an empty array + KVSTORE_API void Clear(); + + // change vector to std::string + KVSTORE_API std::string ToString() const; + + // comparison. Returns value: + // < 0 if "*this" < "blob", + // == 0 if "*this" == "blob", + // > 0 if "*this" > "blob" + KVSTORE_API int Compare(const Blob &blob) const; + + // Return true if "blob" is a prefix of "*this" + KVSTORE_API bool StartsWith(const Blob &blob) const; + + // Write a parcelable object to the given parcel. + // The object position is saved into Parcel if set asRemote_ to + // true, and this intends to use in kernel data transaction. + // Returns true being written on success or false if any error occur. + KVSTORE_API bool Marshalling(Parcel &parcel) const override; + + // get data from the given parcel into this parcelable object. + KVSTORE_API static Blob *Unmarshalling(Parcel &parcel); + + /* write blob size and data to memory buffer. return error when bufferLeftSize not enough. */ + bool WriteToBuffer(uint8_t *&cursorPtr, int &bufferLeftSize) const; + + /* read a blob from memory buffer. */ + bool ReadFromBuffer(const uint8_t *&cursorPtr, int &bufferLeftSize); +private: + std::vector blob_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // DISTRIBUTED_KV_BLOB_H diff --git a/interfaces/innerkits/distributeddata/include/change_notification.h b/interfaces/innerkits/distributeddata/include/change_notification.h new file mode 100644 index 000000000..5ae6cb332 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/change_notification.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 CHANGE_NOTIFICATION_H +#define CHANGE_NOTIFICATION_H + +#include +#include "types.h" +#include "parcel.h" + +namespace OHOS { +namespace DistributedKv { +class ChangeNotification final : public virtual Parcelable { +public: + // Constructor of ChangeNotification. + ChangeNotification(const std::list &insertEntries, + const std::list &updateEntries, + const std::list &deleteEntries, + const std::string &deviceId, + const bool isClear); + + KVSTORE_API ~ChangeNotification(); + + // Get all inserted entries in this change. + KVSTORE_API const std::list &GetInsertEntries() const; + + // Get all updated entries in this changing. + KVSTORE_API const std::list &GetUpdateEntries() const; + + // Get all deleted entries in this changing. + KVSTORE_API const std::list &GetDeleteEntries() const; + + // Get the device ID. + KVSTORE_API const std::string &GetDeviceId() const; + + // Check if this change is made by calling the Clear function. + KVSTORE_API bool IsClear() const; + + // Write a parcelable object to the given parcel. + // The object position is saved into Parcel if asRemote_ is set to + // true, and this intends to use in kernel data transaction. + // Returns true if the writing is successful; returns false otherwise. + KVSTORE_API bool Marshalling(Parcel &parcel) const override; + + // Unmarshall the given parcel from this parcelable object. + KVSTORE_API static ChangeNotification *Unmarshalling(Parcel &parcel); +private: + std::list insertEntries_; + + std::list updateEntries_; + + std::list deleteEntries_; + + std::string deviceId_; + + bool isClear_ = false; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // CHANGE_NOTIFICATION_H diff --git a/interfaces/innerkits/distributeddata/include/data_query.h b/interfaces/innerkits/distributeddata/include/data_query.h new file mode 100755 index 000000000..b2282e56f --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/data_query.h @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_DATA_QUERY_H +#define DISTRIBUTED_DATA_QUERY_H + +#include +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class DataQuery { +public: + KVSTORE_API DataQuery(); + + KVSTORE_API ~DataQuery() = default; + + // Reset the query. + // Return: + // This Query. + KVSTORE_API DataQuery& Reset(); + + // Equal to int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& EqualTo(const std::string &field, const int value); + + // Equal to long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& EqualTo(const std::string &field, const int64_t value); + + // Equal to double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& EqualTo(const std::string &field, const double value); + + // Equal to String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& EqualTo(const std::string &field, const std::string &value); + + // Equal to boolean value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& EqualTo(const std::string &field, const bool value); + + // Not equal to int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& NotEqualTo(const std::string &field, const int value); + + // Not equal to long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& NotEqualTo(const std::string &field, const int64_t value); + + // Not equal to double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& NotEqualTo(const std::string &field, const double value); + + // Not equal to String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& NotEqualTo(const std::string &field, const std::string &value); + + // Not equal to boolean value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& NotEqualTo(const std::string &field, const bool value); + + // Greater than int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThan(const std::string &field, const int value); + + // Greater than long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThan(const std::string &field, const int64_t value); + + // Greater than double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThan(const std::string &field, const double value); + + // Greater than String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThan(const std::string &field, const std::string &value); + + // Less than int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThan(const std::string &field, const int value); + + // Less than long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThan(const std::string &field, const int64_t value); + + // Less than double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThan(const std::string &field, const double value); + + // Less than String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThan(const std::string &field, const std::string &value); + + // Greater than or equal to int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThanOrEqualTo(const std::string &field, const int value); + + // Greater than or equal to long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThanOrEqualTo(const std::string &field, const int64_t value); + + // Greater than or equal to double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThanOrEqualTo(const std::string &field, const double value); + + // Greater than or equal to String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& GreaterThanOrEqualTo(const std::string &field, const std::string &value); + + // Less than or equal to int value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThanOrEqualTo(const std::string &field, const int value); + + // Less than or equal to long value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThanOrEqualTo(const std::string &field, const int64_t value); + + // Less than or equal to double value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThanOrEqualTo(const std::string &field, const double value); + + // Less than or equal to String value. + // Parameters: + // field: the field name. + // value: the field value. + // Return: + // This Query. + KVSTORE_API DataQuery& LessThanOrEqualTo(const std::string &field, const std::string &value); + + // Is null field value. + // Parameters: + // field: the field name. + // Return: + // This Query. + KVSTORE_API DataQuery& IsNull(const std::string &field); + + // Is not null field value. + // Parameters: + // field: the field name. + // Return: + // This Query. + KVSTORE_API DataQuery& IsNotNull(const std::string &field); + + // In int value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& InInt(const std::string &field, const std::vector &valueList); + + // In long value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& InLong(const std::string &field, const std::vector &valueList); + + // In Double value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& InDouble(const std::string &field, const std::vector &valueList); + + // In String value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& InString(const std::string &field, const std::vector &valueList); + + // Not in int value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& NotInInt(const std::string &field, const std::vector &valueList); + + // Not in long value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& NotInLong(const std::string &field, const std::vector &valueList); + + // Not in Double value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& NotInDouble(const std::string &field, const std::vector &valueList); + + // Not in String value list. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& NotInString(const std::string &field, const std::vector &valueList); + + // Like String value. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& Like(const std::string &field, const std::string &value); + + // Unlike String value. + // Parameters: + // field: the field name. + // value: the field value list. + // Return: + // This Query. + KVSTORE_API DataQuery& Unlike(const std::string &field, const std::string &value); + + // And operator. + // Return: + // This Query. + KVSTORE_API DataQuery& And(); + + // Or operator. + // Return: + // This Query. + KVSTORE_API DataQuery& Or(); + + // Order by ascent. + // Parameters: + // field: the field name. + // Return: + // This Query. + KVSTORE_API DataQuery& OrderByAsc(const std::string &field); + + // Order by descent. + // Parameters: + // field: the field name. + // Return: + // This Query. + KVSTORE_API DataQuery& OrderByDesc(const std::string &field); + + // Limit result size. + // Parameters: + // number: the number of results. + // offset: the start position. + // Return: + // This Query. + KVSTORE_API DataQuery& Limit(const int number, const int offset); + + // Begin group. + // Return: + // This Query. + KVSTORE_API DataQuery& BeginGroup(); + + // End group. + // Return: + // This Query. + KVSTORE_API DataQuery& EndGroup(); + + // Select results with specified key prefix. + // Parameters: + // prefix: key prefix. + // Return: + // This Query. + KVSTORE_API DataQuery& KeyPrefix(const std::string &prefix); + + // Select results with suggested index. + // Parameters: + // index: suggested index. + // Return: + // This Query. + KVSTORE_API DataQuery& SetSuggestIndex(const std::string &index); + + // Get string representation + // Return: + // String representation of this query. + KVSTORE_API std::string ToString() const; + + // equal to + static const std::string EQUAL_TO; + + // not equal to + static const std::string NOT_EQUAL_TO; + + // greater than + static const std::string GREATER_THAN; + + // less than + static const std::string LESS_THAN; + + // greater than or equal to + static const std::string GREATER_THAN_OR_EQUAL_TO; + + // less than or equal to + static const std::string LESS_THAN_OR_EQUAL_TO; + + // is null + static const std::string IS_NULL; + + // in + static const std::string IN; + + // not in + static const std::string NOT_IN; + + // like + static const std::string LIKE; + + // not like + static const std::string NOT_LIKE; + + // and + static const std::string AND; + + // or + static const std::string OR; + + // order by asc + static const std::string ORDER_BY_ASC; + + // order by desc + static const std::string ORDER_BY_DESC; + + // limit + static const std::string LIMIT; + + // space + static const std::string SPACE; + + // '^' + static const std::string SPECIAL; + + // '^' escape + static const std::string SPECIAL_ESCAPE; + + // space escape + static const std::string SPACE_ESCAPE; + + // empty string + static const std::string EMPTY_STRING; + + // start in + static const std::string START_IN; + + // end in + static const std::string END_IN; + + // begin group + static const std::string BEGIN_GROUP; + + // end group + static const std::string END_GROUP; + + // key prefix + static const std::string KEY_PREFIX; + + // device id + static const std::string DEVICE_ID; + + // is not null + static const std::string IS_NOT_NULL; + + // type string + static const std::string TYPE_STRING; + + // type integer + static const std::string TYPE_INTEGER; + + // type long + static const std::string TYPE_LONG; + + // type double + static const std::string TYPE_DOUBLE; + + // type boolean + static const std::string TYPE_BOOLEAN; + + // value true + static const std::string VALUE_TRUE; + + // value false + static const std::string VALUE_FALSE; + + // suggested index + static const std::string SUGGEST_INDEX; +private: + std::string str_; + + template + void AppendCommon(const std::string &keyword, const std::string &fieldType, std::string &field, const T &value); + + void AppendCommonString(const std::string &keyword, const std::string &fieldType, + std::string &field, std::string &value); + + void AppendCommonBoolean(const std::string &keyword, const std::string &fieldType, + std::string &field, const bool &value); + + void AppendCommonString(const std::string &keyword, std::string &field, std::string &value); + + template + void AppendCommonList(const std::string &keyword, const std::string &fieldType, + std::string &field, const std::vector &valueList); + + void AppendCommonListString(const std::string &keyword, const std::string &fieldType, + std::string &field, std::vector &valueList); + + void EscapeSpace(std::string &input); + + bool ValidateField(const std::string &field); + + bool ValidateValue(const std::string &value); + + bool ValidateStringValueList(const std::vector &valueList); + + template + std::string BasicToString(const T &value); +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // DISTRIBUTED_DATA_QUERY_H diff --git a/interfaces/innerkits/distributeddata/include/device_status_change_listener.h b/interfaces/innerkits/distributeddata/include/device_status_change_listener.h new file mode 100644 index 000000000..9eb503217 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/device_status_change_listener.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_STATUS_CHANGE_LISTENER_H +#define DEVICE_STATUS_CHANGE_LISTENER_H + +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +class DeviceStatusChangeListener { +public: + KVSTORE_API virtual ~DeviceStatusChangeListener() {}; + KVSTORE_API virtual void OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const = 0; + KVSTORE_API virtual DeviceFilterStrategy GetFilterStrategy() const = 0; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // DEVICE_STATUS_CHANGE_LISTENER_H diff --git a/interfaces/innerkits/distributeddata/include/distributed_kv_data_manager.h b/interfaces/innerkits/distributeddata/include/distributed_kv_data_manager.h new file mode 100644 index 000000000..09ebd8e8a --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/distributed_kv_data_manager.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. + */ + +#ifndef DISTRIBUTED_KV_DATA_MANAGER_H +#define DISTRIBUTED_KV_DATA_MANAGER_H + +#include "kvstore.h" +#include "kvstore_death_recipient.h" +#include "kvstore_observer.h" +#include "single_kvstore.h" +#include "types.h" +#include "device_status_change_listener.h" + +namespace OHOS { +namespace DistributedKv { +class DistributedKvDataManager final { +public: + KVSTORE_API + DistributedKvDataManager(); + + KVSTORE_API + ~DistributedKvDataManager(); + + // Open kvstore instance with the given storeId, creating it if needed. + // It is allowed to open the same kvstore concurrently + // multiple times, but only one KvStoreImpl will be created. + // Parameters: + // options: the config of the kvstore, including encrypt, + // create if needed and whether need sync between devices. + // appId: the name of the application. + // :storeId: the name of the kvstore. + // callback: including status and KvStore instance returned by this call. + // callback will return: + // if Options.createIfMissing is false and kvstore has not been created before, + // STORE_NOT_FOUND and nullptr, + // if storeId is not valid, INVALID_ARGUMENT and nullptr, + // if appId has no permission, PERMISSION_DENIED and nullptr, + // otherwise, SUCCESS and the unipue_ptr of kvstore, which client can use to operate kvstore, will be returned. + KVSTORE_API void GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback); + + // Open kvstore instance with the given storeId, creating it if needed. + // It is allowed to open the same kvstore concurrently + // multiple times, but only one KvStoreImpl will be created. + // Parameters: + // options: the config of the kvstore, including encrypt, + // create if needed and whether need sync between devices. + // appId: the name of the application. + // :storeId: the name of the kvstore. + // callback: including status and KvStore instance returned by this call. + // callback will return: + // if Options.createIfMissing is false and kvstore has not been created before, + // STORE_NOT_FOUND and nullptr, + // if storeId is not valid, INVALID_ARGUMENT and nullptr, + // if appId has no permission, PERMISSION_DENIED and nullptr, + // otherwise, SUCCESS and the unipue_ptr of kvstore, which client can use to operate kvstore, will be returned. + KVSTORE_API void GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback); + + // get all existed kvstore names. + KVSTORE_API void GetAllKvStoreId(const AppId &appId, std::function &)> callback); + + // WARNING: try to close a KvStore while other thread(s) still using it may cause process crash. + // Disconnect kvstore instance from kvstoreimpl with the given storeId, + // if all kvstore created for a single kvsotreimpl, kvstoreimpl and resource below will be freed. + // before this call, all KvStoreSnapshot must be released firstly, + // otherwise this call will fail. + // after this call, kvstore and kvstoresnapshot become invalid. + // call to it will return nullptr exception. + // Parameters: + // appId: the name of the application. + // storeId: the name of the kvstore. + KVSTORE_API + Status CloseKvStore(const AppId &appId, const StoreId &storeId, std::unique_ptr kvStorePtr = nullptr); + + // WARNING: try to close a KvStore while other thread(s) still using it may cause process crash. + // + // Disconnect kvstore instance from kvstoreimpl. + // if all kvstore created for a single kvsotreimpl, kvstoreimpl and resource below will be freed. + // before this call, all KvStoreResultSet must be released firstly, + // otherwise this call will fail. + // after this call, kvstore and KvStoreResultSet become invalid. + // call to it will return nullptr exception. + // Parameters: + // appId: the name of the application. + // kvStorePtr: the pointer of the kvstore. + KVSTORE_API + Status CloseKvStore(const AppId &appId, std::unique_ptr kvStorePtr); + + // WARNING: try to close a KvStore while other thread(s) still using it may cause process crash. + // close all opened kvstores for this appId. + KVSTORE_API Status CloseAllKvStore(const AppId &appId); + + // delete kvstore file with the given storeId. + // client should first close all connections to it and then delete it, + // otherwise delete may return error. + // after this call, kvstore and kvstoresnapshot become invalid. + // call to it will return error. + // Parameters: + // appId: the name of the application. + // storeId: the name of the kvstore. + KVSTORE_API Status DeleteKvStore(const AppId &appId, const StoreId &storeId); + + // delete all kvstore. + KVSTORE_API Status DeleteAllKvStore(const AppId &appId); + + KVSTORE_API void RegisterKvStoreServiceDeathRecipient(std::shared_ptr kvStoreDeathRecipient); + + KVSTORE_API + void UnRegisterKvStoreServiceDeathRecipient(std::shared_ptr kvStoreDeathRecipient); + + // Subscribe device status change, like online or offline. + // Client should override AppDeviceStatusChangeListener and register it by this function, observer->OnDeviceChanged + // will be called on remote device status change. + // Parameters: + // observer: callback for device status change event. + // Return: + // Status of this subscribe operation. + KVSTORE_API Status StartWatchDeviceChange(std::shared_ptr observer); + + // Unsubscribe device status change, like online or offline. + // client should override AppDeviceStatusChangeListener and register it by calling this function, then + // observer->OnDeviceChanged will no longer be called on remote device status change. + // Parameters: + // observer: callback for device status change event. + // Return: + // Status of this unsubscribe operation. + KVSTORE_API Status StopWatchDeviceChange(std::shared_ptr observer); + + // Get all connected devices. + // Client can use this method to retrieve all devices that have already connected, + // and then call StartWatchDeviceChange to watch device status change later. + // Parameters: + // deviceInfoList: list of all connected device will be returned by this parameter. + // Return: + // Status of this get device list operation. + KVSTORE_API Status GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy); + + // Get device. + // Client can use this method to retrieve local device, + // Parameters: + // localDevice: DeviceInfo will be returned by this parameter. + // Return: + // Status of this get device operation. + KVSTORE_API Status GetLocalDevice(DeviceInfo &localDevice); +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTED_KV_DATA_MANAGER_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore.h b/interfaces/innerkits/distributeddata/include/kvstore.h new file mode 100644 index 000000000..21eacdcd0 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore.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 KVSTORE_H +#define KVSTORE_H + +#include "kvstore_observer.h" +#include "kvstore_snapshot.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +class KvStore { +public: + KVSTORE_API KvStore() = default; + + // forbidden copy constructor. + KvStore(const KvStore &) = delete; + KvStore &operator=(const KvStore &) = delete; + + KVSTORE_API virtual ~KvStore() + {} + + // Get kvstore name of this kvstore instance. + KVSTORE_API virtual StoreId GetStoreId() const = 0; + + // Creates a snapshot of the kvstore, allowing the client app to read a + // consistent data of the content of the kvstore. + // + // If observer is provided, it will receive notifications for changes of the + // kvstore newer than the resulting snapshot. + // Parameters: + // observer: observer for subscribe. + // callback: including status and KvStoreSnapshot instance returned by this call. + KVSTORE_API + virtual void GetKvStoreSnapshot(std::shared_ptr observer, + std::function)> callback) const = 0; + + // Release snapshot created by calling GetKvStoreSnapshot. + KVSTORE_API virtual Status ReleaseKvStoreSnapshot(std::unique_ptr kvStoreSnapshotPtr) = 0; + + // Mutation operations. + // Key level operations. + // Mutations are bundled together into atomic commits. If a transaction is in + // progress, the list of mutations bundled together is tied to the current + // transaction. If no transaction is in progress, mutations will be a unique transaction. + // Put one entry with key-value into kvstore, + // key length should not be greater than 256, and can not be empty. + // value size should be less than IPC transport limit, and can not be empty. + KVSTORE_API virtual Status Put(const Key &key, const Value &value) = 0; + + // see Put, PutBatch put a list of entries to kvstore, + // all entries will be put in a transaction, + // if entries contains invalid entry, PutBatch will all fail. + // entries's size should be less than 128 and memory size must be less than IPC transport limit. + KVSTORE_API virtual Status PutBatch(const std::vector &entries) = 0; + + // delete one entry in the kvstore, + // delete non-exist key still return KEY NOT FOUND error, + // key length should not be greater than 256, and can not be empty. + KVSTORE_API virtual Status Delete(const Key &key) = 0; + + // delete a list of entries in the kvstore, + // delete key not exist still return success, + // key length should not be greater than 256, and can not be empty. + // if keys contains invaid key, all delete will fail. + // keys memory size should not be greater than IPC transport limit, and can not be empty. + KVSTORE_API virtual Status DeleteBatch(const std::vector &keys) = 0; + + // clear all entries in the kvstore. + // after this call, IsClear function in ChangeNotification in subscription return true. + KVSTORE_API virtual Status Clear() = 0; + + // start transaction. + // all changes to this kvstore will be in a same transaction and will not change the store until Commit() or + // Rollback() is called. + // before this transaction is committed or rollbacked, all attemption to close this store will fail. + KVSTORE_API virtual Status StartTransaction() = 0; + + // commit current transaction. all changes to this store will be done after calling this method. + // any calling of this method outside a transaction will fail. + KVSTORE_API virtual Status Commit() = 0; + + // rollback current transaction. + // all changes to this store during this transaction will be rollback after calling this method. + // any calling of this method outside a transaction will fail. + KVSTORE_API virtual Status Rollback() = 0; + + // subscribe kvstore to watch data change in the kvstore, + // OnChange in he observer will be called when data changed, with all the changed contents. + // client is responsible for free observer after and only after call UnSubscribeKvStore. + // otherwise, codes in sdk may use a freed memory and cause unexpected result. + // Parameters: + // subscribeType: strategy for this subscribe, default right now. + // observer: callback client provided, client must implement KvStoreObserver and override OnChange function, when + // data changed in store, OnChange will called in Observer. + KVSTORE_API + virtual Status SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) = 0; + + // unSubscribe kvstore to un-watch data change in the kvstore, + // after this call, no message will be received even data change in the kvstore. + // client is responsible for free observer after and only after call UnSubscribeKvStore. + // otherwise, codes in sdk may use a freed memory and cause unexpected result. + // Parameters: + // subscribeType: strategy for this subscribe, default right now. + // observer: callback client provided in SubscribeKvStore. + KVSTORE_API + virtual Status UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore_death_recipient.h b/interfaces/innerkits/distributeddata/include/kvstore_death_recipient.h new file mode 100644 index 000000000..a066affe0 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore_death_recipient.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_DEATH_RECIPIENT_H +#define KVSTORE_DEATH_RECIPIENT_H + +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreDeathRecipient { +public: + KVSTORE_API virtual void OnRemoteDied() = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_DEATH_RECIPIENT_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore_observer.h b/interfaces/innerkits/distributeddata/include/kvstore_observer.h new file mode 100644 index 000000000..8efc299ce --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore_observer.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_OBSERVER_H +#define KVSTORE_OBSERVER_H + +#include +#include "change_notification.h" +#include "kvstore_snapshot.h" + +namespace OHOS { +namespace DistributedKv { +// client implement this class to watch kvstore change. +class KvStoreObserver { +public: + KVSTORE_API KvStoreObserver() = default; + + KVSTORE_API virtual ~KvStoreObserver() + {} + + // client override this function to receive change notification. + KVSTORE_API + virtual void OnChange(const ChangeNotification &changeNotification, std::unique_ptr snapshot) = 0; + + // client override this function to receive change notification. + KVSTORE_API + virtual void OnChange(const ChangeNotification &changeNotification) + {} +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_OBSERVER_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore_result_set.h b/interfaces/innerkits/distributeddata/include/kvstore_result_set.h new file mode 100644 index 000000000..6eef60140 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore_result_set.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 KVSTORE_RESULT_SET_H +#define KVSTORE_RESULT_SET_H + +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreResultSet { +public: + KVSTORE_API virtual ~KvStoreResultSet() + {} + + // Returns the count of rows in the result set. + KVSTORE_API virtual int GetCount() const = 0; + + // Returns the current read position of the result set. + KVSTORE_API virtual int GetPosition() const = 0; + + // Move the read position to the first row, return false if the result set is empty. + KVSTORE_API virtual bool MoveToFirst() = 0; + + // Move the read position to the last row, return false if the result set is empty. + KVSTORE_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. + KVSTORE_API virtual bool MoveToNext() = 0; + + // Move the read position to the previous row, + // return false if result set is empty or the read position is already before the first entry in the result set. + KVSTORE_API virtual bool MoveToPrevious() = 0; + + // Move the read position by a relative amount from the current position. + KVSTORE_API virtual bool Move(int offset) = 0; + + // Move the read position to an absolute position value. + KVSTORE_API virtual bool MoveToPosition(int position) = 0; + + // Returns whether the read position is pointing to the first row. + KVSTORE_API virtual bool IsFirst() const = 0; + + // Returns whether the read position is pointing to the last row. + KVSTORE_API virtual bool IsLast() const = 0; + + // Returns whether the read position is before the first row. + KVSTORE_API virtual bool IsBeforeFirst() const = 0; + + // Returns whether the read position is after the last row. + KVSTORE_API virtual bool IsAfterLast() const = 0; + + // Get a key-value entry. + KVSTORE_API virtual Status GetEntry(Entry &entry) const = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_RESULT_SET_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore_snapshot.h b/interfaces/innerkits/distributeddata/include/kvstore_snapshot.h new file mode 100755 index 000000000..3116c4990 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore_snapshot.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 KVSTORE_SNAPSHOT_H +#define KVSTORE_SNAPSHOT_H + +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreSnapshot { +public: + KVSTORE_API KvStoreSnapshot() = default; + + KVSTORE_API virtual ~KvStoreSnapshot() + {} + + // Deprecated. use the GetEntries interface without nextKey as parameter instead. + // Get a list of entries from kvstore by keyPrefix, + // key length must be less than 1024, + // GetEntries will return all entries whose Key.StartsWith(keyPrefix) is true, + // if keyPrefix is empty, all entries in the kvstore will be returned. + // if data size is larger than 800k, data may be transported by several times. each time callback will give you the + // first key of the not-transported part. You can use this key as nextKey to get next part of data. When you get an + // empty nextKey, It means all data has been transported. + // parameters: + // prefixKey: perfix key to search + // nextKey: The first key to start in this search. + // callback: all entries satisfied perfixKey, status of this call and the first key of the next part of data. + [[deprecated]] + KVSTORE_API virtual void GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) = 0; + + // Get a list of entries from kvstore by keyPrefix, + // key length must be less than 1024, + // GetEntries will return all entries whose Key.StartsWith(keyPrefix) is true, + // if keyPrefix is empty, all entries in the kvstore will be returned. + // if some entry in the return set large then 750k, GetEntries may only return entries before this entry. you need + // to use GetKeys interface to get all keys, then use Get interface to get each entry. + // parameters: + // prefixKey: perfix key to search + // callback: all entries satisfies perfixKey, and Stauts for this call. + KVSTORE_API + virtual void GetEntries(const Key &prefixKey, std::function &)> callback) = 0; + + // Deprecated. use the GetKeys interface without nextKey as parameter instead. + // Get a list of keys from kvstore by keyPrefix, + // key length must be less than 1024, + // GetKeys will return all keys whose Key.StartsWith(keyPrefix) is true, + // if keyPrefix is empty, all keys in the kvstore will be returned. + // if data size is larger than 800k, data may be transported by several times. each time callback will give you the + // first key of the not-transported part. You can use this key as nextKey to get next part of data. When you get an + // empty nextKey, It means all data has been transported. + // parameters: + // prefixKey: perfix key to search + // nextKey: The first key to start in this search. + // callback: all keys satisfies perfixKey, status of this call and the first key of the next part of data. + [[deprecated]] + KVSTORE_API virtual void GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) = 0; + + // Get a list of keys from kvstore by keyPrefix, + // key length must be less than 1024, + // GetKeys will return all keys whose Key.StartsWith(keyPrefix) is true, + // if keyPrefix is empty, all keys in the kvstore will be returned. + // parameters: + // prefixKey: perfix key to search + // callback: all keys satisfies perfixKey, and Stauts for this call. + KVSTORE_API + virtual void GetKeys(const Key &prefixKey, std::function &)> callback) = 0; + + // Get value by key from kvstore, key length must be less than 256 and can not be empty. + // if key not found in kvstore, KEY_NOT_FOUND will be returned. + // otherwise, SUCCESS will be returned and value can be retrieved from the second parameter. + // parameters: + // key: key specified by client, + // value: value stored in kvstore, or empty and KEY_NOT_FOUND returned. + KVSTORE_API virtual Status Get(const Key &key, Value &value) = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_SNAPSHOT_H diff --git a/interfaces/innerkits/distributeddata/include/kvstore_sync_callback.h b/interfaces/innerkits/distributeddata/include/kvstore_sync_callback.h new file mode 100644 index 000000000..3500199d5 --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/kvstore_sync_callback.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 KVSTORE_SYNC_CALLBACK_H +#define KVSTORE_SYNC_CALLBACK_H + +#include +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +// client implememt this class to watch kvstore change. +class KvStoreSyncCallback { +public: + KVSTORE_API KvStoreSyncCallback() = default; + + KVSTORE_API virtual ~KvStoreSyncCallback() + {} + + // This virtual function will be called on sync callback. + // Client needs to override this function to receive sync results. + // Parameters: + // results: sync results for devices set in Sync function. + KVSTORE_API virtual void SyncCompleted(const std::map &results) = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_SYNC_CALLBACK_H diff --git a/interfaces/innerkits/distributeddata/include/single_kvstore.h b/interfaces/innerkits/distributeddata/include/single_kvstore.h new file mode 100755 index 000000000..1a2161fba --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/single_kvstore.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_KV_STORE_H +#define SINGLE_KV_STORE_H + +#include +#include "kvstore.h" +#include "kvstore_observer.h" +#include "kvstore_result_set.h" +#include "kvstore_sync_callback.h" +#include "types.h" +#include "data_query.h" + +namespace OHOS { +namespace DistributedKv { +// This is a public interface. Implementation of this class is in AppKvStoreImpl. +// This class provides put, delete, search, sync and subscribe functions of a key-value store. +class SingleKvStore { +public: + KVSTORE_API SingleKvStore() = default; + + KVSTORE_API virtual ~SingleKvStore() + {} + + // Get all entries in this store which key start with prefixKey. + // Parameters: + // perfixkey: the prefix to be searched. + // entries: entries will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + KVSTORE_API virtual Status GetEntries(const Key &prefixKey, std::vector &entries) const = 0; + + // Get all entries in this store by query. + // Parameters: + // query: the query string. + // entries: entries will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + virtual Status GetEntriesWithQuery(const std::string &query, std::vector &entries) const = 0; + + // Get all entries in this store by query. + // Parameters: + // query: the query object. + // entries: entries will be returned in this parameter. + // Return: + // Status of this GetEntries operation. + KVSTORE_API virtual Status GetEntriesWithQuery(const DataQuery &query, std::vector &entries) const = 0; + + // Get ResultSet in this store which key start with prefixKey. + // Parameters: + // perfixkey: the prefix to be searched. + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetResultSet operation. + KVSTORE_API virtual void GetResultSet(const Key &prefixKey, + std::function)> callback) const = 0; + + // Get ResultSet in this store by Query. + // Parameters: + // query: the query string. + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetResultSet operation. + virtual void GetResultSetWithQuery(const std::string &query, + std::function)> callback) const = 0; + + // Get ResultSet in this store by Query. + // Parameters: + // query: the query object. + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this GetResultSet operation. + KVSTORE_API virtual void GetResultSetWithQuery(const DataQuery &query, + std::function)> callback) const = 0; + + // Close the ResultSet returned by GetResultSet. + // Parameters: + // resultSet: resultSet will be returned in this parameter. + // Return: + // Status of this CloseResultSet operation. + KVSTORE_API virtual Status CloseResultSet(std::unique_ptr resultSet) = 0; + + // Get the number of result by query. + // Parameters: + // query: the query string. + // result: result will be returned in this parameter. + // Return: + // Status of this CloseResultSet operation. + virtual Status GetCountWithQuery(const std::string &query, int &result) const = 0; + + // Get the number of result by query. + // Parameters: + // query: the query object. + // result: result will be returned in this parameter. + // Return: + // Status of this CloseResultSet operation. + KVSTORE_API virtual Status GetCountWithQuery(const DataQuery &query, int &result) const = 0; + + // Sync store with other devices. This is an asynchronous method, + // sync will fail if there is a syncing operation in progress. + // Parameters: + // deviceIdList: device list to sync. + // mode: mode can be set to SyncMode::PUSH, SyncMode::PULL and SyncMode::PUTH_PULL. PUSH_PULL will firstly + // push all not-local store to listed devices, then pull these stores back. + // allowedDelayMs: allowed delay milli-second to sync. default value is 0 for compatibility. + // Return: + // Status of this Sync operation. + KVSTORE_API virtual Status Sync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t allowedDelayMs = 0) = 0; + + // Remove the device data synced from remote. + // Parameters: + // device: device id. + // Return: + // Status of this remove operation. + KVSTORE_API virtual Status RemoveDeviceData(const std::string &device) = 0; + + // Get id of this SingleKvStore. + KVSTORE_API virtual StoreId GetStoreId() const = 0; + + // Delete an entry by its key. + // Parameters: + // key: key of the entry to be deleted. + // Return: + // Status of this delete operation. + KVSTORE_API virtual Status Delete(const Key &key) = 0; + + // Write a pair of key and value to this store. + // Parameters: + // key: key of this entry. Should be less than 1024 bytes. key will be trimmed before store. + // value: value of this entry. Should be less than (4 * 1024 * 1024) bytes. + // Return: + // Status of this put operation. + KVSTORE_API virtual Status Put(const Key &key, const Value &value) = 0; + + // Get value from AppKvStore by its key. + // Parameters: + // key: key of this entry. + // value: value will be returned in this parameter. + // Return: + // Status of this get operation. + KVSTORE_API virtual Status Get(const Key &key, Value &value) = 0; + + // subscribe change of this kvstore to a client-defined observer. observer->OnChange method will be called when store + // changes. + // Parameters: + // subscribeType: SUBSCRIBE_TYPE_LOCAL means local changes of syncable kv store, + // : SUBSCRIBE_TYPE_REMOTE means synced data changes from remote devices, + // : SUBSCRIBE_TYPE_ALL means both local changes and synced data changes. + // observer: observer to subscribe changes. + // Return: + // Status of this subscribe operation. + KVSTORE_API + virtual Status SubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) = 0; + + // un-subscribe change of this kvstore to a client-defined observer. + // Parameters: + // subscribeType: SUBSCRIBE_TYPE_LOCAL means local changes of syncable kv store, + // : SUBSCRIBE_TYPE_REMOTE means synced data changes from remote devices, + // : SUBSCRIBE_TYPE_ALL means both local changes and synced data changes. + // observer: observer to subscribe changes. + // Return: + // Status of this subscribe operation. + KVSTORE_API + virtual Status UnSubscribeKvStore(SubscribeType subscribeType, std::shared_ptr observer) = 0; + + // register message for sync operation. + // Parameters: + // callback: callback to register. + // Return: + // Status of this register operation. + KVSTORE_API + virtual Status RegisterSyncCallback(std::shared_ptr callback) = 0; + + // un-register message for sync operation. + // Parameters: + // callback: callback to register. + // Return: + // Status of this register operation. + KVSTORE_API + virtual Status UnRegisterSyncCallback() = 0; + + // see Put, PutBatch put a list of entries to kvstore, + // all entries will be put in a transaction, + // if entries contains invalid entry, PutBatch will all fail. + // entries's size should be less than 128 and memory size must be less than IPC transport limit. + KVSTORE_API virtual Status PutBatch(const std::vector &entries) = 0; + + // delete a list of entries in the kvstore, + // delete key not exist still return success, + // key length should not be greater than 256, and can not be empty. + // if keys contains invalid key, all delete will fail. + // keys memory size should not be greater than IPC transport limit, and can not be empty. + KVSTORE_API virtual Status DeleteBatch(const std::vector &keys) = 0; + + // start transaction. + // all changes to this kvstore will be in a same transaction and will not change the store until Commit() or + // Rollback() is called. + // before this transaction is committed or rollbacked, all attemption to close this store will fail. + KVSTORE_API virtual Status StartTransaction() = 0; + + // commit current transaction. all changes to this store will be done after calling this method. + // any calling of this method outside a transaction will fail. + KVSTORE_API virtual Status Commit() = 0; + + // rollback current transaction. + // all changes to this store during this transaction will be rollback after calling this method. + // any calling of this method outside a transaction will fail. + KVSTORE_API virtual Status Rollback() = 0; + + // set synchronization parameters of this store. + // Parameters: + // syncParam: sync policy parameter. + // Return: + // Status of this operation. + KVSTORE_API virtual Status SetSyncParam(const KvSyncParam &syncParam) = 0; + + // get synchronization parameters of this store. + // Parameters: + // syncParam: sync policy parameter. + // Return: + // Status of this operation. + KVSTORE_API virtual Status GetSyncParam(KvSyncParam &syncParam) = 0; + + KVSTORE_API virtual Status SetCapabilityEnabled(bool enabled) const = 0; + + KVSTORE_API virtual Status SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) const = 0; + + KVSTORE_API virtual Status GetSecurityLevel(SecurityLevel &securityLevel) const = 0; +protected: + // control this store. + // Parameters: + // inputParam: input parameter. + // output: output data, nullptr if no data is returned. + // Return: + // Status of this control operation. + KVSTORE_API virtual Status Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // SINGLE_KV_STORE_H diff --git a/interfaces/innerkits/distributeddata/include/types.h b/interfaces/innerkits/distributeddata/include/types.h new file mode 100755 index 000000000..b44a413aa --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/types.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_KVSTORE_TYPES_H +#define DISTRIBUTED_KVSTORE_TYPES_H + +#include +#include +#include +#include +#include "blob.h" +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { + +// key set by client, can be any non-empty bytes array, and less than 1024 size. +using Key = OHOS::DistributedKv::Blob; + +// value set by client, can be any bytes array. +using Value = OHOS::DistributedKv::Blob; + +// user identifier from user-account +struct UserId { + std::string userId; +}; + +// app identifier from Bms +struct AppId { + std::string appId; +}; + +// kvstore name set by client by calling GetKvStore, +// storeId len must be less or equal than 256, +// and can not be empty and all space. +struct StoreId { + std::string storeId; +}; + +struct KvStoreTuple { + std::string userId; + std::string appId; + std::string storeId; +}; + +struct AppThreadInfo { + std::int32_t pid; + std::int32_t uid; +}; + +// distributed_data_manager using sub error code 3 +constexpr ErrCode DISTRIBUTEDDATAMGR_ERR_OFFSET = ErrCodeOffset(SUBSYS_DISTRIBUTEDDATAMNG, 3); +enum class Status { + SUCCESS = ERR_OK, + ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET, + INVALID_ARGUMENT = DISTRIBUTEDDATAMGR_ERR_OFFSET + 1, + ILLEGAL_STATE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 2, + SERVER_UNAVAILABLE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 3, + STORE_NOT_OPEN = DISTRIBUTEDDATAMGR_ERR_OFFSET + 4, + STORE_NOT_FOUND = DISTRIBUTEDDATAMGR_ERR_OFFSET + 5, + STORE_ALREADY_SUBSCRIBE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 6, + STORE_NOT_SUBSCRIBE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 7, + KEY_NOT_FOUND = DISTRIBUTEDDATAMGR_ERR_OFFSET + 8, + DB_ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET + 9, + NETWORK_ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET + 10, + NO_DEVICE_CONNECTED = DISTRIBUTEDDATAMGR_ERR_OFFSET + 11, + PERMISSION_DENIED = DISTRIBUTEDDATAMGR_ERR_OFFSET + 12, + IPC_ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET + 13, + CRYPT_ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET + 14, + TIME_OUT = DISTRIBUTEDDATAMGR_ERR_OFFSET + 15, + DEVICE_NOT_FOUND = DISTRIBUTEDDATAMGR_ERR_OFFSET + 16, + NOT_SUPPORT = DISTRIBUTEDDATAMGR_ERR_OFFSET + 17, + SCHEMA_MISMATCH = DISTRIBUTEDDATAMGR_ERR_OFFSET + 18, + INVALID_SCHEMA = DISTRIBUTEDDATAMGR_ERR_OFFSET + 19, + READ_ONLY = DISTRIBUTEDDATAMGR_ERR_OFFSET + 20, + INVALID_VALUE_FIELDS = DISTRIBUTEDDATAMGR_ERR_OFFSET + 21, + INVALID_FIELD_TYPE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 22, + CONSTRAIN_VIOLATION = DISTRIBUTEDDATAMGR_ERR_OFFSET + 23, + INVALID_FORMAT = DISTRIBUTEDDATAMGR_ERR_OFFSET + 24, + INVALID_QUERY_FORMAT = DISTRIBUTEDDATAMGR_ERR_OFFSET + 25, + INVALID_QUERY_FIELD = DISTRIBUTEDDATAMGR_ERR_OFFSET + 26, + SYSTEM_ACCOUNT_EVENT_PROCESSING = DISTRIBUTEDDATAMGR_ERR_OFFSET + 27, + RECOVER_SUCCESS = DISTRIBUTEDDATAMGR_ERR_OFFSET + 28, + RECOVER_FAILED = DISTRIBUTEDDATAMGR_ERR_OFFSET + 29, + MIGRATION_KVSTORE_FAILED = DISTRIBUTEDDATAMGR_ERR_OFFSET + 30, + EXCEED_MAX_ACCESS_RATE = DISTRIBUTEDDATAMGR_ERR_OFFSET + 31, + SECURITY_LEVEL_ERROR = DISTRIBUTEDDATAMGR_ERR_OFFSET + 32, +}; + +enum class SubscribeType { + DEFAULT = 0, // default let bms delete + SUBSCRIBE_TYPE_LOCAL = 1, // local changes of syncable kv store + SUBSCRIBE_TYPE_REMOTE = 2, // synced data changes from remote devices + SUBSCRIBE_TYPE_ALL = 3, // both local changes and synced data changes +}; + +struct Entry : public virtual Parcelable { + Key key; + Value value; + // Write a parcelable object to the given parcel. + // The object position is saved into Parcel if set savePosition to + // true, and this intends to use in kernel data transaction. + // Returns size being written on success or zero if any error occur. + bool Marshalling(Parcel &parcel) const override + { + if (!parcel.WriteParcelable(&key)) { + return false; + } + if (!parcel.WriteParcelable(&value)) { + return false; + } + return true; + } + + // Read data from the given parcel into this parcelable object. + // Returns size being read on success or zero if any error occur. + static Entry *Unmarshalling(Parcel &parcel) + { + Entry *entry = new Entry; + + bool noError = true; + sptr keyTmp = parcel.ReadParcelable(); + if (keyTmp != nullptr) { + entry->key = *keyTmp; + } else { + noError = false; + } + + sptr valueTmp = parcel.ReadParcelable(); + if (valueTmp != nullptr) { + entry->value = *valueTmp; + } else { + noError = false; + } + + if (!noError) { + delete entry; + entry = nullptr; + } + return entry; + } + + KVSTORE_API virtual ~Entry() + {} +}; + +enum class SyncPolicy { + LOW, + MEDIUM, + HIGH, + HIGHTEST, +}; + +enum class SyncMode { + PULL, + PUSH, + PUSH_PULL, +}; + +enum KvStoreType : int32_t { + DEVICE_COLLABORATION, + SINGLE_VERSION, + MULTI_VERSION, + INVALID_TYPE, +}; + +enum SecurityLevel : int { + NO_LABEL, + S0, + S1, + S2, + S3_EX, + S3, + S4, +}; + +enum class KvControlCmd { + SET_SYNC_PARAM = 1, + GET_SYNC_PARAM, +}; + +using KvParam = OHOS::DistributedKv::Blob; + +struct KvSyncParam { + uint32_t allowedDelayMs { 0 }; +}; + +enum class DeviceChangeType { + DEVICE_OFFLINE = 0, + DEVICE_ONLINE = 1, +}; + +struct DeviceInfo { + std::string deviceId; + std::string deviceName; + std::string deviceType; + + bool Marshalling(Parcel &data) const + { + data.WriteString(deviceId); + data.WriteString(deviceName); + data.WriteString(deviceType); + return true; + } + + static DeviceInfo* UnMarshalling(Parcel &data) + { + auto deviceInfoPtr = new (std::nothrow)DeviceInfo(); + if (deviceInfoPtr != nullptr) { + data.ReadString(deviceInfoPtr->deviceId); + data.ReadString(deviceInfoPtr->deviceName); + data.ReadString(deviceInfoPtr->deviceType); + } + return deviceInfoPtr; + } +}; + +enum class DeviceFilterStrategy { + FILTER = 0, + NO_FILTER = 1, +}; + +struct Options { + bool createIfMissing = true; + bool encrypt = false; + bool persistant = false; + bool backup = true; + bool autoSync = true; + int securityLevel = SecurityLevel::NO_LABEL; + SyncPolicy syncPolicy = SyncPolicy::HIGH; + KvStoreType kvStoreType = KvStoreType::DEVICE_COLLABORATION; + bool syncable = true; // let bms delete first + std::string schema = ""; + bool dataOwnership = true; // true indicates the ownership of distributed data is DEVICE, otherwise, ACCOUNT +}; + +template +std::vector TransferTypeToByteArray(const T &t) +{ + return std::vector(reinterpret_cast(const_cast(&t)), + reinterpret_cast(const_cast(&t)) + sizeof(T)); +} + +template +T TransferByteArrayToType(const std::vector &blob) +{ + // replace assert to HILOG_FATAL when HILOG_FATAL is ok. + if (blob.size() != sizeof(T) || blob.size() == 0) { + constexpr int tSize = sizeof(T); + uint8_t tContent[tSize] = { 0 }; + return *reinterpret_cast(tContent); + } + return *reinterpret_cast(const_cast(&blob[0])); +} +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTED_KVSTORE_TYPES_H diff --git a/interfaces/innerkits/distributeddata/include/visibility.h b/interfaces/innerkits/distributeddata/include/visibility.h new file mode 100644 index 000000000..af8fa9b3d --- /dev/null +++ b/interfaces/innerkits/distributeddata/include/visibility.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif diff --git a/ohos.build b/ohos.build new file mode 100755 index 000000000..4fe6e2d64 --- /dev/null +++ b/ohos.build @@ -0,0 +1,66 @@ +{ + "subsystem": "distributeddatamgr", + "parts": { + "distributeddatamgr": { + "variants": [ + "phone", + "wearable", + "ivi" + ], + "module_list": [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata:app_distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:build_module", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app:build_module", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:build_module" + ], + "inner_kits": [ + { + "name": "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata", + "header": { + "header_files": [ + "blob.h", + "change_notification.h", + "distributed_kv_data_manager.h", + "kvstore.h", + "kvstore_death_recipient.h", + "kvstore_observer.h", + "kvstore_result_set.h", + "kvstore_snapshot.h", + "kvstore_sync_callback.h", + "single_kvstore.h", + "types.h", + "visibility.h", + "data_query.h", + "device_status_change_listener.h" + ], + "header_base": "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include" + } + }, + { + "name": "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata:app_distributeddata", + "header": { + "header_files": [ + "app_blob.h", + "app_change_notification.h", + "app_distributed_kv_data_manager.h", + "app_kvstore.h", + "app_kvstore_conflict_data.h", + "app_kvstore_observer.h", + "app_kvstore_result_set.h", + "app_device_status_change_listener.h", + "app_types.h", + "app_kvstore_corruption_observer.h", + "visibility.h" + ], + "header_base": "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include" + } + } + ], + "system_kits": [], + "test_list": [ + "//foundation/distributeddatamgr/distributeddatamgr:build_native_test" + ] + } + } +} + diff --git a/services/distributeddataservice/adapter/BUILD.gn b/services/distributeddataservice/adapter/BUILD.gn new file mode 100755 index 000000000..f62f96109 --- /dev/null +++ b/services/distributeddataservice/adapter/BUILD.gn @@ -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. +import("//build/ohos.gni") +import("//build/ohos_var.gni") + +config("distributeddata_adapter_private_config") { + visibility = [ ":*" ] + include_dirs = [] + + cflags = [ "-Wno-multichar" ] + + cflags_cc = [ "-fvisibility=hidden" ] +} + +config("distributeddata_adapter_public_config") { + visibility = [ "//foundation/distributeddatamgr/distributeddatamgr:*" ] + + include_dirs = [ + "include/log", + "include/dfx", + "include/communicator", + "include/autils", + "include/utils", + "include", + "include/security", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include/", + ] +} + +ohos_shared_library("distributeddata_adapter") { + sources = [] + + configs = [ ":distributeddata_adapter_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/autils:distributeddata_autils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/communicator:distributeddata_communicator_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/dfx:distributeddata_dfx_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/security:distributeddata_security_static", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + public_configs = [ ":distributeddata_adapter_public_config" ] + + subsystem_name = "distributeddatamgr" +} diff --git a/services/distributeddataservice/adapter/LICENSE b/services/distributeddataservice/adapter/LICENSE new file mode 100644 index 000000000..4947287f7 --- /dev/null +++ b/services/distributeddataservice/adapter/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/services/distributeddataservice/adapter/account/BUILD.gn b/services/distributeddataservice/adapter/account/BUILD.gn new file mode 100755 index 000000000..8caa97f9e --- /dev/null +++ b/services/distributeddataservice/adapter/account/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_account_static") { + sources = [ + "src/account_delegate.cpp", + "src/account_delegate_impl.cpp", + ] + + include_dirs = [ + "../include/account", + "../include/permission", + "../include/utils", + "./src", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "../utils:distributeddata_utils_static", + "//utils/native/base:utils", + ] + + external_deps = [ + "appexecfwk_standard:appexecfwk_base", + + # "ces:libcommonevent", + "aafwk_standard:base", + "aafwk_standard:intent", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "os_account_standard:libaccountkits", + ] +} diff --git a/services/distributeddataservice/adapter/account/src/account_delegate.cpp b/services/distributeddataservice/adapter/account/src/account_delegate.cpp new file mode 100755 index 000000000..e2734df68 --- /dev/null +++ b/services/distributeddataservice/adapter/account/src/account_delegate.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 "account_delegate_impl.h" + +namespace OHOS { +namespace DistributedKv { +const std::string AccountDelegate::MAIN_DEVICE_ACCOUNT_ID = "0"; + +AccountDelegate *AccountDelegate::GetInstance() +{ + if (getInstance_ == nullptr) { + return nullptr; + } + return getInstance_(); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/account/src/account_delegate_impl.cpp b/services/distributeddataservice/adapter/account/src/account_delegate_impl.cpp new file mode 100755 index 000000000..89aa501be --- /dev/null +++ b/services/distributeddataservice/adapter/account/src/account_delegate_impl.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EVENT_HANDLER" + +#include "account_delegate_impl.h" +#include +#include +#include "constant.h" +#include "crypto_utils.h" +#include "ohos_account_kits.h" +#include "permission_validator.h" + +namespace OHOS { +namespace DistributedKv { +AccountDelegate::BaseInstance AccountDelegate::getInstance_ = AccountDelegateImpl::GetBaseInstance; + +AccountDelegateImpl *AccountDelegateImpl::GetInstance() +{ + static AccountDelegateImpl accountDelegate; + return &accountDelegate; +} + +AccountDelegate *AccountDelegateImpl::GetBaseInstance() +{ + return AccountDelegateImpl::GetInstance(); +} + +AccountDelegateImpl::~AccountDelegateImpl() +{ + ZLOGE("destruct"); + observerMap_.Clear(); +} + +void AccountDelegateImpl::SubscribeAccountEvent() +{ + +} + +std::string AccountDelegateImpl::GetCurrentHarmonyAccountId(const std::string &bundleName) const +{ + ZLOGD("start"); + if (!bundleName.empty() && PermissionValidator::IsAutoLaunchEnabled(bundleName)) { + return Constant::DEFAULT_GROUP_ID; + } + auto ohosAccountInfo = AccountSA::OhosAccountKits::GetInstance().QueryOhosAccountInfo(); + if (!ohosAccountInfo.first) { + ZLOGE("get ohosAccountInfo from OhosAccountKits is null, return default"); + return AccountSA::DEFAULT_OHOS_ACCOUNT_UID; + } + if (ohosAccountInfo.second.uid_.empty()) { + ZLOGE("get ohosAccountInfo from OhosAccountKits is null, return default"); + return AccountSA::DEFAULT_OHOS_ACCOUNT_UID; + } + + return CryptoUtils::Sha256UserId(ohosAccountInfo.second.uid_); +} + +std::string AccountDelegateImpl::GetDeviceAccountIdByUID(int32_t uid) const +{ + return std::to_string(AccountSA::OhosAccountKits::GetInstance().GetDeviceAccountIdByUID(uid)); +} + +void AccountDelegateImpl::NotifyAccountChanged(const AccountEventInfo &accountEventInfo) +{ + observerMap_.ForEach([&](std::string key, std::shared_ptr val) { + val->OnAccountChanged(accountEventInfo); + }); +} + +Status AccountDelegateImpl::Subscribe(std::shared_ptr observer) +{ + ZLOGD("start"); + if (observer == nullptr || observer->Name().empty()) { + return Status::INVALID_ARGUMENT; + } + if (observerMap_.ContainsKey(observer->Name())) { + return Status::INVALID_ARGUMENT; + } + + auto ret = observerMap_.Put(observer->Name(), observer); + if (ret) { + ZLOGD("end"); + return Status::SUCCESS; + } + ZLOGE("fail"); + return Status::ERROR; +} + +Status AccountDelegateImpl::Unsubscribe(std::shared_ptr observer) +{ + ZLOGD("start"); + if (observer == nullptr || observer->Name().empty()) { + return Status::INVALID_ARGUMENT; + } + if (!observerMap_.ContainsKey(observer->Name())) { + return Status::INVALID_ARGUMENT; + } + + auto ret = observerMap_.Delete(observer->Name()); + if (ret) { + ZLOGD("end"); + return Status::SUCCESS; + } + ZLOGD("fail"); + return Status::ERROR; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/account/src/account_delegate_impl.h b/services/distributeddataservice/adapter/account/src/account_delegate_impl.h new file mode 100755 index 000000000..3c706bfcb --- /dev/null +++ b/services/distributeddataservice/adapter/account/src/account_delegate_impl.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 DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_IMPL_H +#define DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_IMPL_H + +#include "account_delegate.h" +#include +#include +#include "concurrent_map.h" +#include "ohos/aafwk/content/intent.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +class AccountDelegateImpl final : public AccountDelegate { +public: + static AccountDelegateImpl *GetInstance(); + static AccountDelegate *GetBaseInstance(); + Status Subscribe(std::shared_ptr observer) override; + Status Unsubscribe(std::shared_ptr observer) override; + std::string GetCurrentHarmonyAccountId(const std::string &bundleName = "") const override; + std::string GetDeviceAccountIdByUID(int32_t uid) const override; + void SubscribeAccountEvent() override; +private: + ~AccountDelegateImpl(); + void NotifyAccountChanged(const AccountEventInfo &accountEventInfo); + + ConcurrentMap> observerMap_ {}; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_IMPL_H diff --git a/services/distributeddataservice/adapter/account/test/BUILD.gn b/services/distributeddataservice/adapter/account/test/BUILD.gn new file mode 100755 index 000000000..c3b0a6581 --- /dev/null +++ b/services/distributeddataservice/adapter/account/test/BUILD.gn @@ -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. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddatafwk" + +############################################################################### +ohos_unittest("DistributeddataAccountTest") { + module_out_path = module_output_path + + sources = [ "account_delegate_test.cpp" ] + include_dirs = [ + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/account", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/log", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/autils", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/utils", + ] + + deps = [ + "../:distributeddata_account_static", + "../../permission:distributeddata_permission_static", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] + + external_deps = [ + "aafwk_standard:base", + "aafwk_standard:intent", + "appexecfwk_standard:appexecfwk_base", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + ] +} + +############################################################################### +group("unittest") { + testonly = true + + deps = [] + + deps += [ ":DistributeddataAccountTest" ] +} +############################################################################### diff --git a/services/distributeddataservice/adapter/account/test/account_delegate_test.cpp b/services/distributeddataservice/adapter/account/test/account_delegate_test.cpp new file mode 100755 index 000000000..21e8f4e76 --- /dev/null +++ b/services/distributeddataservice/adapter/account/test/account_delegate_test.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. + */ + +#define LOG_TAG "AccountDelegateTest" +#include +#include +#include "account_delegate.h" +#include "log_print.h" + +using namespace OHOS::DistributedKv; +using namespace testing::ext; +class AccountDelegateTest : public testing::Test { +}; + +class AccountObserver : public AccountDelegate::Observer { +public: + void OnAccountChanged(const AccountEventInfo &info) + { + } + // must specify unique name for observer + std::string Name() + { + return "accountTestObserver"; + } +}; +/** +* @tc.name: Test001 +* @tc.desc: Test account login, logout, unbind; +* @tc.type: FUNC +* @tc.require: AR000D08K2 AR000CQDUM +* @tc.author: hongbo +*/ +HWTEST_F(AccountDelegateTest, Test001, TestSize.Level0) +{ + ZLOGD("start:"); + auto account = AccountDelegate::GetInstance(); + auto observer = std::make_shared(); + ZLOGD("observer:"); + account->Subscribe(observer); + ZLOGD("observer subscribed"); + + account->Unsubscribe(observer); + ASSERT_TRUE(true); +} + +/** +* @tc.name: Test002 +* @tc.desc: Test getaccount +* @tc.type: FUNC +* @tc.require: AR000D487D +* @tc.author: hongbo +*/ +HWTEST_F(AccountDelegateTest, Test002, TestSize.Level0) +{ + auto account = AccountDelegate::GetInstance(); + auto id = account->MAIN_DEVICE_ACCOUNT_ID; + ZLOGD("observer subscribed %s", id.c_str()); + ASSERT_TRUE(!id.empty()); +} diff --git a/services/distributeddataservice/adapter/autils/BUILD.gn b/services/distributeddataservice/adapter/autils/BUILD.gn new file mode 100755 index 000000000..b51d7a4a1 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_autils_static") { + sources = [ + "src/constant.cpp", + "src/directory_utils.cpp", + "src/kv_scheduler.cpp", + "src/thread_pool/kv_store_task.cpp", + "src/thread_pool/kv_store_thread.cpp", + "src/thread_pool/kv_store_thread_pool.cpp", + "src/thread_pool/kv_store_thread_pool_impl.cpp", + ] + + include_dirs = [ + "../include/autils", + "../include/log", + "//utils/native/base/include", + "../include/dfx", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ + "//utils/native/base:utils", + ] + + external_deps = [ + "bytrace_standard:bytrace_core", + "hiviewdfx_hilog_native:libhilog", + ] +} diff --git a/services/distributeddataservice/adapter/autils/src/constant.cpp b/services/distributeddataservice/adapter/autils/src/constant.cpp new file mode 100755 index 000000000..475f1ba73 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/constant.cpp @@ -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. + */ + +#include "constant.h" +#include +#include +#include + +namespace OHOS { +namespace DistributedKv { +// the Key Prefix for Meta data of KvStore. +const std::string KvStoreMetaRow::KEY_PREFIX = "KvStoreMetaData"; + +const std::string SecretMetaRow::KEY_PREFIX = "SecretKey"; + +/* version for distributed kv data service. */ +const std::string Constant::VERSION = "1"; + +/* meta name for distributed kv data service. */ +const std::string Constant::META_DIR_NAME = "Meta"; + +/* name for distributed kv data service. */ +const std::string Constant::SERVICE_NAME = "mdds"; + +/* root path for distributed kv data service. */ +const std::string Constant::ROOT_PATH = "/data/misc_de/0"; + +/* root path for distributeddata service and system services. */ +const std::string Constant::ROOT_PATH_DE = "/data/misc_de/0"; + +/* root path for self-developed and non-self-developed app. */ +const std::string Constant::ROOT_PATH_CE = "/data/misc_ce/0"; + +// the max length for key is 1024. +const size_t Constant::MAX_KEY_LENGTH = 1024; + +// the max length for StoreId is 128. +const size_t Constant::MAX_STORE_ID_LENGTH = 128; + +// the max length for value is 4M. +const size_t Constant::MAX_VALUE_LENGTH = 4 * 1024 * 1024; + +// the max batch for putBatch is 128. +const size_t Constant::MAX_BATCH_SIZE = 128; + +// the max capacity for ipc is 800K. +const size_t Constant::MAX_IPC_CAPACITY = 800 * 1024; + +// the default mode is 0755, stands for r/w/x for user, r/x for group, r/x for others. +const mode_t Constant::DEFAULT_MODE = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + +// the mode for dir is 0755, r/w/x for user, r/-/x for group, r/-/x for others. +const mode_t Constant::DEFAULT_MODE_DIR = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + +// the mode for file is 0600, r/w/- for user, -/-/- for group, -/-/- for others. +const mode_t Constant::DEFAULT_MODE_FILE = S_IRUSR | S_IWUSR; + +// Size threshold of switching to large data is a little smaller than MAX_IPC_CAPACITY. +const int Constant::SWITCH_RAW_DATA_SIZE = 700 * 1024; + +const int Constant::MAX_OPEN_KVSTORES = 16; + +// default group id for synchronization. +const std::string Constant::DEFAULT_GROUP_ID = "default"; + +// true indicates the ownership of distributed data is DEVICE, otherwise, ACCOUNT +const bool Constant::STOREID_ONLY_FLAG = true; + +// service meta db name. +const std::string Constant::SERVICE_META_DB_NAME = "service_meta"; + +const std::string Constant::KEY_SEPARATOR = "###"; + +const std::string Constant::PROCESS_LABEL = "distributeddata"; + +const std::string Constant::ROOT_KEY_GENERATED = "RootKeyGenerated"; + +std::vector KvStoreMetaRow::GetKeyFor(const std::string &key) +{ + std::string str = Constant::Concatenate({KvStoreMetaRow::KEY_PREFIX, Constant::KEY_SEPARATOR, key }); + return std::vector(str.begin(), str.end()); +} + +std::vector SecretMetaRow::GetKeyFor(const std::string &key) +{ + std::string str = Constant::Concatenate({SecretMetaRow::KEY_PREFIX, Constant::KEY_SEPARATOR, key }); + return std::vector(str.begin(), str.end()); +} + +std::string Constant::Concatenate(std::initializer_list stringList) +{ + std::string result; + size_t result_size = 0; + for (const std::string &str : stringList) { + result_size += str.size(); + } + result.reserve(result_size); + for (const std::string &str : stringList) { + result.append(str.data(), str.size()); + } + return result; +} + +std::string Constant::GetDefaultDeviceAccountId() +{ + return "0"; +} + +std::string Constant::GetDefaultHarmonyAccountName() +{ + return "default"; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/autils/src/directory_utils.cpp b/services/distributeddataservice/adapter/autils/src/directory_utils.cpp new file mode 100755 index 000000000..333a4b175 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/directory_utils.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DirectoryUtils" + +#include +#include +#include "directory_utils.h" +#include "log_print.h" +#include "unistd.h" + +namespace OHOS { +namespace DistributedKv { +// change the mode of all files in the specified directory recursively. +bool DirectoryUtils::ChangeModeFileOnly(const std::string &path, const mode_t &mode) +{ + ZLOGI("begin."); + std::string subPath; + bool ret = true; + DIR *dir = opendir(path.c_str()); + if (dir == nullptr) { + return false; + } + + while (true) { + struct dirent *ptr = readdir(dir); + if (ptr == nullptr) { + break; + } + + // skip current directory and parent directory. + if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { + continue; + } + + subPath = IncludeDelimiterAtPathTail(path) + std::string(ptr->d_name); + if (ptr->d_type == DT_DIR) { + ret = ChangeModeFileOnly(subPath, mode); + continue; + } + + // change the mode of file only. + if ((access(subPath.c_str(), F_OK) == 0) && (ptr->d_type == DT_REG)) { + ZLOGD("[Von-Debug]change the file[%s] to mode[%d].", subPath.c_str(), mode); + if (!ChangeMode(subPath, mode)) { + closedir(dir); + ZLOGD("[Von-Debug]change the file[%s] to mode[%d] failed.", subPath.c_str(), mode); + return false; + } + } + } + closedir(dir); + return ret; +} + +// change the mode of all subdirectories in the specified directory recursively. +bool DirectoryUtils::ChangeModeDirOnly(const std::string &path, const mode_t &mode) +{ + ZLOGI("begin."); + std::string subPath; + bool ret = true; + DIR *dir = opendir(path.c_str()); + if (dir == nullptr) { + return false; + } + + while (true) { + struct dirent *ptr = readdir(dir); + if (ptr == nullptr) { + break; + } + + // skip current directory and parent directory. + if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { + continue; + } + + subPath = IncludeDelimiterAtPathTail(path) + std::string(ptr->d_name); + if (ptr->d_type == DT_DIR) { + ret = ChangeModeDirOnly(subPath, mode); + continue; + } + + // change the mode of directory only. + if ((access(subPath.c_str(), F_OK) == 0) && (ptr->d_type == DT_DIR)) { + ZLOGD("[Von-Debug]change the dir[%s] to mode[%d].", subPath.c_str(), mode); + if (!ChangeMode(subPath, mode)) { + closedir(dir); + ZLOGD("[Von-Debug]change the dir[%s] to mode[%d] failed.", subPath.c_str(), mode); + return false; + } + } + } + closedir(dir); + + std::string currentPath = ExcludeDelimiterAtPathTail(path); + if (access(currentPath.c_str(), F_OK) == 0) { + if (!ChangeMode(currentPath, mode)) { + return false; + } + } + return ret; +} + +// create all subdirectories in the specified directory recursively. +bool DirectoryUtils::CreateDirectory(const std::string &path) +{ + std::string::size_type index = 0; + do { + std::string subPath; + index = path.find('/', index + 1); + if (index == std::string::npos) { + subPath = path; + } else { + subPath = path.substr(0, index); + } + + if (access(subPath.c_str(), F_OK) != 0) { + if (mkdir(subPath.c_str(), (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) { + return false; + } + } + } while (index != std::string::npos); + + return access(path.c_str(), F_OK) == 0; +} + +// exclude separators '/' at the end of the path. +std::string DirectoryUtils::ExcludeDelimiterAtPathTail(const std::string &path) +{ + if (path.rfind('/') != path.size() - 1) { + return path; + } + + if (!path.empty()) { + return path.substr(0, static_cast(path.size()) - 1); + } + + return path; +} + +// include separators '/' at the end of the path. +std::string DirectoryUtils::IncludeDelimiterAtPathTail(const std::string &path) +{ + if (path.rfind('/') != path.size() - 1) { + return path + "/"; + } + + return path; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/autils/src/kv_scheduler.cpp b/services/distributeddataservice/adapter/autils/src/kv_scheduler.cpp new file mode 100755 index 000000000..60816bbcf --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/kv_scheduler.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. + */ + +#include "kv_scheduler.h" + +namespace OHOS::DistributedKv { +KvScheduler::KvScheduler() +{ + isRunning_ = true; + thread_ = std::make_unique([this]() { this->Loop(); }); +} + +KvScheduler::~KvScheduler() +{ + isRunning_ = false; + At(std::chrono::system_clock::now(), [](){}); + thread_->join(); +} + +SchedulerTask KvScheduler::At(const std::chrono::system_clock::time_point &time, std::function task) +{ + std::unique_lock lock(mutex_); + auto it = kvTasks_.insert({time, task}); + if (it == kvTasks_.begin()) { + condition_.notify_one(); + } + return it; +} + +void KvScheduler::Every(const std::chrono::system_clock::duration interval, std::function task) +{ + std::function waitFunc = [this, interval, task]() { + task(); + this->Every(interval, task); + }; + At(std::chrono::system_clock::now() + interval, waitFunc); +} + +void KvScheduler::Every(std::chrono::system_clock::duration delay, std::chrono::system_clock::duration interval, + std::function func) +{ + std::function waitFunc = [this, interval, func]() { + func(); + this->Every(interval, func); + }; + At(std::chrono::system_clock::now() + delay, waitFunc); +} + +void KvScheduler::Every(int times, std::chrono::system_clock::duration delay, + std::chrono::system_clock::duration interval, std::function func) +{ + std::function waitFunc = [this, times, interval, func]() { + func(); + int count = times; + count--; + if (times > 1) { + this->Every(count, interval, interval, func); + } + }; + + At(std::chrono::system_clock::now() + delay, waitFunc); +} + +void KvScheduler::Remove(const std::map>::iterator &task) +{ + std::unique_lock lock(mutex_); + auto it = kvTasks_.find(task->first); + if (it != kvTasks_.end()) { + kvTasks_.erase(it); + condition_.notify_one(); + } +} + +void KvScheduler::Loop() +{ + while (isRunning_) { + std::function exec; + { + std::unique_lock lock(mutex_); + if ((!kvTasks_.empty()) && (kvTasks_.begin()->first <= std::chrono::system_clock::now())) { + exec = kvTasks_.begin()->second; + kvTasks_.erase(kvTasks_.begin()); + } + } + + if (exec) { + exec(); + } + + std::unique_lock lock(mutex_); + if (kvTasks_.empty()) { + condition_.wait(lock); + } else { + condition_.wait_until(lock, kvTasks_.begin()->first); + } + } +} +} // namespace OHOS::DistributedKv diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_task.cpp b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_task.cpp new file mode 100755 index 000000000..b6de8ace9 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_task.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. + */ + +#define LOG_TAG "KvStoreTask" + +#include "kv_store_task.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreTask::KvStoreTask(std::function lambda) +{ + task_ = std::move(lambda); + name_ = std::string(); +} + +KvStoreTask::KvStoreTask(std::function lambda, const std::string &taskName) +{ + task_ = std::move(lambda); + name_ = taskName; +} + +void KvStoreTask::operator()() +{ + task_(); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.cpp b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.cpp new file mode 100755 index 000000000..db78730f5 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.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. + */ + +#define LOG_TAG "KvStoreThread" + +#include "kv_store_thread_pool_impl.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreThread::KvStoreThread(KvStoreThreadPool *threadPool) + : pool_(threadPool) +{ + realThread_ = std::thread([&, threadPool]() { + // this makes me unconfortable: this lambda capture 'this' by reference, and right after this call this object + // is move-constructed, so when we call this in Run(), we are actually refer to the old object. we can still + // use all its non-virtual function, but all arguments and virtual-function are not available. + Run(threadPool); + }); +} + +KvStoreThread::KvStoreThread(KvStoreThread &&thread) + : pool_(thread.pool_) +{ + realThread_ = std::move(thread.realThread_); +} + +void KvStoreThread::Run(KvStoreThreadPool *pool) +{ + if (pool == nullptr) { + ZLOGW("input param is null."); + return; + } + + auto impl = reinterpret_cast(pool); + while (impl->IsRunning()) { + impl->ScheduleTask()(); + } + ZLOGW("stop"); +} + +void KvStoreThread::Join() +{ + realThread_.join(); +} + +KvStoreThread::~KvStoreThread() +{} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.h b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.h new file mode 100644 index 000000000..ae483e072 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread.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 KV_STORE_THREAD_H +#define KV_STORE_THREAD_H + +#include +#include "kv_store_thread_pool.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreThread { +public: + explicit KvStoreThread(KvStoreThreadPool *threadPool); + KvStoreThread(KvStoreThread &&thread); + KvStoreThread(const KvStoreThread &) = delete; + KvStoreThread &operator=(KvStoreThread &&) = delete; + KvStoreThread &operator=(const KvStoreThread &) = delete; + void Run(KvStoreThreadPool *threadPool); + void Join(); + ~KvStoreThread(); +private: + KvStoreThreadPool *pool_; + std::thread realThread_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // KV_STORE_THREAD_H diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool.cpp b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool.cpp new file mode 100755 index 000000000..1c71755c1 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreThreadPool" + +#include "kv_store_thread_pool_impl.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +std::shared_ptr KvStoreThreadPool::GetPool(int poolSize, bool startImmediately) +{ + std::shared_ptr poolImpl = + std::make_shared(poolSize, startImmediately); + if (poolImpl == nullptr) { + return nullptr; + } + return std::shared_ptr(std::dynamic_pointer_cast(poolImpl)); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.cpp b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.cpp new file mode 100755 index 000000000..06a2035ef --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreThreadPoolImpl" + +#include "kv_store_thread_pool_impl.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreThreadPoolImpl::~KvStoreThreadPoolImpl() +{ + Stop(); +} + +void KvStoreThreadPoolImpl::Start() +{ + ZLOGI("start"); + running = true; + for (int i = 0; i < threadNum; i++) { + threadList.push_back(KvStoreThread(this)); + } +} + +void KvStoreThreadPoolImpl::Stop() +{ + ZLOGW("stop"); + if (!running) { + return; + } + { + std::unique_lock lock(taskListMutex); + running = false; + for (auto task = taskList.begin(); task != taskList.end(); task++) { + ZLOGI("running task in stop()"); + (*task)(); + ZLOGI("running task finish"); + } + taskList.clear(); + } + has_task.notify_all(); + for (auto thread = threadList.begin(); thread != threadList.end(); thread++) { + thread->Join(); + } +} + +bool KvStoreThreadPoolImpl::IsRunning() const +{ + return running; +} + +KvStoreThreadPoolImpl::KvStoreThreadPoolImpl(int threadNum, bool startImmediately) + : taskList(), threadList(), threadNum(threadNum) +{ + if (threadNum <= 0 || threadNum > MAX_POOL_SIZE) { + this->threadNum = DEFAULT_POOL_SIZE; + } + if (startImmediately) { + Start(); + } +} + +bool KvStoreThreadPoolImpl::AddTask(KvStoreTask &&task) +{ + ZLOGD("start"); + if (threadList.empty()) { + Start(); + } + std::unique_lock lock(taskListMutex); + if (!running) { + return false; + } + taskList.push_back(std::move(task)); + has_task.notify_one(); + return true; +} + +KvStoreTask KvStoreThreadPoolImpl::ScheduleTask() +{ + std::unique_lock lock(taskListMutex); + if (taskList.empty() && running) { + has_task.wait(lock, [&]() {return !running || !taskList.empty(); }); + } + if (taskList.empty()) { + ZLOGW("taskList empty. schedule empty task(pool stopping?)"); + return KvStoreTask([]() {;}); + } + KvStoreTask ret = std::move(taskList.front()); + taskList.pop_front(); + return ret; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.h b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.h new file mode 100644 index 000000000..c09bae7a2 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/src/thread_pool/kv_store_thread_pool_impl.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 KV_STORE_THREAD_POOL_IMPL_H +#define KV_STORE_THREAD_POOL_IMPL_H + +#include +#include +#include +#include "kv_store_thread_pool.h" +#include "kv_store_thread.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreThreadPoolImpl : public KvStoreThreadPool { +public: + KvStoreThreadPoolImpl(int threadNum, bool startImmediately); + KvStoreThreadPoolImpl() = delete; + KvStoreThreadPoolImpl(const KvStoreThreadPoolImpl &) = delete; + KvStoreThreadPoolImpl(KvStoreThreadPoolImpl &&) = delete; + KvStoreThreadPoolImpl& operator=(const KvStoreThreadPoolImpl &) = delete; + KvStoreThreadPoolImpl& operator=(KvStoreThreadPoolImpl &&) = delete; + bool AddTask(KvStoreTask &&task) override; + void Stop() final; + KvStoreTask ScheduleTask(); + bool IsRunning() const; + virtual ~KvStoreThreadPoolImpl(); +private: + std::mutex taskListMutex{}; + std::list taskList{}; + std::condition_variable has_task{}; + std::list threadList{}; + int threadNum; + void Start(); + bool running = false; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // KV_STORE_THREAD_POOL_IMPL_H diff --git a/services/distributeddataservice/adapter/autils/test/BUILD.gn b/services/distributeddataservice/adapter/autils/test/BUILD.gn new file mode 100755 index 000000000..0012bb19b --- /dev/null +++ b/services/distributeddataservice/adapter/autils/test/BUILD.gn @@ -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. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddatafwk" + +############################################################################### +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ "../../include/autils/" ] +} + +ohos_unittest("KvStoreThreadPoolTest") { + module_out_path = module_output_path + + sources = [ + "unittest/kv_scheduler_test.cpp", + "unittest/kv_store_thread_pool_test.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "../../autils:distributeddata_autils_static", + "//third_party/googletest:gtest_main", + ] +} + +group("unittest") { + testonly = true + + deps = [] + + deps += [ ":KvStoreThreadPoolTest" ] +} +############################################################################### diff --git a/services/distributeddataservice/adapter/autils/test/unittest/concurrent_map_test.cpp b/services/distributeddataservice/adapter/autils/test/unittest/concurrent_map_test.cpp new file mode 100755 index 000000000..ffb49ec0a --- /dev/null +++ b/services/distributeddataservice/adapter/autils/test/unittest/concurrent_map_test.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 +#include "concurrent_map.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class ConcurrentMapTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void ConcurrentMapTest::SetUpTestCase(void) +{} + +void ConcurrentMapTest::TearDownTestCase(void) +{} + +void ConcurrentMapTest::SetUp(void) +{} + +void ConcurrentMapTest::TearDown(void) +{} + +/** + * @tc.name: Test001 + * @tc.desc: test concurrent map CURD operation. + * @tc.type: FUNC + * @tc.require: AR000CQS31 + * @tc.author: hongbo + */ +HWTEST_F(ConcurrentMapTest, Test001, TestSize.Level0) +{ + ConcurrentMap cmap; + + int size = cmap.Size(); + ASSERT_TRUE(size == 0); + + std::string temp = "abc"; + cmap.Put(temp, temp); + + std::string value; + auto val = cmap.Get(temp, value); + ASSERT_TRUE(val); + ASSERT_STREQ(temp.c_str(), value.c_str()); + + auto isContained = cmap.ContainsKey(temp); + ASSERT_TRUE(isContained); + + ASSERT_TRUE(cmap.Delete(temp)); + + ASSERT_TRUE(cmap.Empty()); + + cmap.Clear(); +} \ No newline at end of file diff --git a/services/distributeddataservice/adapter/autils/test/unittest/kv_scheduler_test.cpp b/services/distributeddataservice/adapter/autils/test/unittest/kv_scheduler_test.cpp new file mode 100755 index 000000000..6b80cd333 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/test/unittest/kv_scheduler_test.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 +#include +#include +#include +#include +#include +#include +#include +#include "kv_scheduler.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class KvSchedulerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvSchedulerTest::SetUpTestCase(void) +{} + +void KvSchedulerTest::TearDownTestCase(void) +{} + +void KvSchedulerTest::SetUp(void) +{} + +void KvSchedulerTest::TearDown(void) +{} + +/** + * @tc.name: KvSchedulerTest001 + * @tc.desc: test schedule at some time. + * @tc.type: FUNC + * @tc.require: SR000DR9J0 AR000DR9J1 + * @tc.author: hongbo + */ +HWTEST_F(KvSchedulerTest, KvSchedulerTest001, TestSize.Level1) +{ + auto sch = std::make_unique(); + ASSERT_TRUE(nullptr != sch) << "sch is nullptr"; + std::chrono::system_clock::time_point tp3 = std::chrono::system_clock::now() + std::chrono::duration(1); + int a = 1; + auto task = sch->At(tp3, [&]() { + a = 2; + }); + std::this_thread::sleep_for(std::chrono::seconds(5)); + EXPECT_EQ(a, 2) << "KvSchedulerTest KvSchedulerTest001 failed"; + ASSERT_TRUE(nullptr != sch) << "sch is nullptr"; + sch->Remove(task); +} + +/** + * @tc.name: KvSchedulerTest002 + * @tc.desc: test schedule at some time. + * @tc.type: FUNC + * @tc.require: SR000DR9J0 AR000DR9J1 + * @tc.author: hongbo + */ +HWTEST_F(KvSchedulerTest, Test002, TestSize.Level1) +{ + auto sch = std::make_unique(); + ASSERT_TRUE(nullptr != sch) << "sch is nullptr"; + std::chrono::duration d(1); + int a = 1; + sch->Every(d, d, [&]() { + a = 2; + }); + std::this_thread::sleep_for(std::chrono::seconds(5)); + EXPECT_EQ(a, 2) << "KvSchedulerTest KvSchedulerTest002 failed"; +} + +/** + * @tc.name: KvSchedulerTest003 + * @tc.desc: test schedule at some time. + * @tc.type: FUNC + * @tc.require: SR000DR9J0 AR000DR9J1 + * @tc.author: hongbo + */ +HWTEST_F(KvSchedulerTest, KvSchedulerTest003, TestSize.Level1) +{ + auto sch = std::make_unique(); + ASSERT_TRUE(nullptr != sch) << "sch is nullptr"; + std::chrono::duration d(1); + int a = 1; + sch->Every(2, d, d, [&]() { + a = a + 1; + }); + std::this_thread::sleep_for(std::chrono::seconds(5)); + EXPECT_EQ(a, 3) << "KvSchedulerTest KvSchedulerTest003 failed"; +} \ No newline at end of file diff --git a/services/distributeddataservice/adapter/autils/test/unittest/kv_store_thread_pool_test.cpp b/services/distributeddataservice/adapter/autils/test/unittest/kv_store_thread_pool_test.cpp new file mode 100644 index 000000000..1369625c5 --- /dev/null +++ b/services/distributeddataservice/adapter/autils/test/unittest/kv_store_thread_pool_test.cpp @@ -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. + */ + +#include +#include +#include +#include +#include "kv_store_thread_pool.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class KvStoreThreadPoolTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvStoreThreadPoolTest::SetUpTestCase(void) +{} + +void KvStoreThreadPoolTest::TearDownTestCase(void) +{} + +void KvStoreThreadPoolTest::SetUp(void) +{} + +void KvStoreThreadPoolTest::TearDown(void) +{} + +/** + * @tc.name: TestApplyTask001 + * @tc.desc: test if task can be done asynchronous. + * @tc.type: FUNC + * @tc.require: AR000CQS31 + * @tc.author: liqiao + */ +HWTEST_F(KvStoreThreadPoolTest, TestApplyTask001, TestSize.Level0) +{ + auto pool = KvStoreThreadPool::GetPool(8, true); + int var = 0; + auto start = std::chrono::system_clock::now(); + pool->AddTask(KvStoreTask([&var](){ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + var++; + })); + auto end = std::chrono::system_clock::now(); + std::chrono::duration returnTime = end - start; + EXPECT_LT(returnTime.count(), 0.05); + EXPECT_EQ(var, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + EXPECT_EQ(var, 1); +} + +/** + * @tc.name: TestApplyTask002 + * @tc.desc: test if task can be done in different thread. + * @tc.type: FUNC + * @tc.require: AR000CQS31 + * @tc.author: liqiao + */ +HWTEST_F(KvStoreThreadPoolTest, TestApplyTask002, TestSize.Level2) +{ + auto pool = KvStoreThreadPool::GetPool(2, false); + int var = 0; + std::mutex varMutex; + auto start = std::chrono::system_clock::now(); + KvStoreTask task([&](){ + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::lock_guard lock(varMutex); + var++; + }); + for (int i = 0; i < 8; i++) { + pool->AddTask(KvStoreTask(task)); + } + auto end = std::chrono::system_clock::now(); + std::chrono::duration returnTime = end - start; + EXPECT_LT(returnTime.count(), 0.1); + EXPECT_EQ(var, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(700)); + EXPECT_EQ(var, 2); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + EXPECT_EQ(var, 4); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + EXPECT_EQ(var, 6); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + EXPECT_EQ(var, 8); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + EXPECT_EQ(var, 8); + pool->Stop(); +} + +/** + * @tc.name: TestApplyTask003 + * @tc.desc: test whether task can be done if they are not scheduled when calling Stop(). + * @tc.type: FUNC + * @tc.require: AR000CQS31 + * @tc.author: liqiao + */ +HWTEST_F(KvStoreThreadPoolTest, TestApplyTask003, TestSize.Level1) +{ + auto pool = KvStoreThreadPool::GetPool(2, false); + int var = 0; + std::mutex varMutex; + KvStoreTask task([&](){ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::lock_guard lock(varMutex); + var++; + }); + for (int i = 0; i < 8; i++) { + pool->AddTask(KvStoreTask(task)); + } + EXPECT_EQ(var, 0); + pool->Stop(); + EXPECT_EQ(var, 8); +} \ No newline at end of file diff --git a/services/distributeddataservice/adapter/broadcaster/BUILD.gn b/services/distributeddataservice/adapter/broadcaster/BUILD.gn new file mode 100755 index 000000000..ef3730e65 --- /dev/null +++ b/services/distributeddataservice/adapter/broadcaster/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_broadcaster_static") { + sources = [ + "src/broadcast_sender.cpp", + "src/broadcast_sender_impl.cpp", + ] + + include_dirs = [ + "../include/broadcaster", + "./src", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ "//utils/native/base:utils" ] + + external_deps = [ + # "ces:libcommonevent", + "aafwk_standard:base", + "aafwk_standard:intent", + "appexecfwk_standard:appexecfwk_base", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + ] +} diff --git a/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender.cpp b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender.cpp new file mode 100755 index 000000000..e350d933d --- /dev/null +++ b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender.cpp @@ -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. + */ + +#include "broadcast_sender.h" +#include +#include "broadcast_sender_impl.h" + +namespace OHOS::DistributedKv { +std::shared_ptr BroadcastSender::instance_; +std::mutex BroadcastSender::mutex_; +std::shared_ptr BroadcastSender::GetInstance() +{ + if (instance_ == nullptr) { + std::lock_guard lock(mutex_); + if (instance_ == nullptr) { + instance_ = std::make_shared(); + } + } + return instance_; +} +} diff --git a/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.cpp b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.cpp new file mode 100755 index 000000000..6d8fddf19 --- /dev/null +++ b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.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. + */ + +#define LOG_TAG "BroadcastSenderImpl" + +#include "broadcast_sender_impl.h" +#include "ohos/aafwk/base/array_wrapper.h" +#include "ohos/aafwk/base/string_wrapper.h" +#include "ohos/aafwk/content/intent.h" + +namespace OHOS::DistributedKv { +void BroadcastSenderImpl::SendEvent(const EventParams ¶ms) +{ + +} +} diff --git a/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.h b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.h new file mode 100644 index 000000000..10e07b6bb --- /dev/null +++ b/services/distributeddataservice/adapter/broadcaster/src/broadcast_sender_impl.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 BROADCAST_SENDER_IMPL_H +#define BROADCAST_SENDER_IMPL_H + +#include "broadcast_sender.h" + +namespace OHOS::DistributedKv { +class BroadcastSenderImpl : public BroadcastSender { +public: + void SendEvent(const EventParams ¶ms) override; +private: + static const inline std::string ACTION_NAME = "DistributedDataMgrStarter"; + static const inline std::string PKG_NAME = "pkgName"; +}; +} +#endif // BROADCAST_SENDER_IMPL_H diff --git a/services/distributeddataservice/adapter/communicator/BUILD.gn b/services/distributeddataservice/adapter/communicator/BUILD.gn new file mode 100755 index 000000000..6ff2901f8 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/BUILD.gn @@ -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. +import("//build/ohos.gni") +ohos_static_library("distributeddata_communicator_static") { + sources = [ + "src/app_device_handler.cpp", + "src/app_device_handler.h", + "src/app_pipe_handler.cpp", + "src/app_pipe_handler.h", + "src/app_pipe_mgr.cpp", + "src/app_pipe_mgr.h", + "src/ark_communication_provider.cpp", + "src/ark_communication_provider.h", + "src/communication_provider.cpp", + "src/communication_provider_impl.cpp", + "src/communication_provider_impl.h", + "src/data_buffer.cpp", + "src/process_communicator_impl.cpp", + "src/softbus_adapter.h", + "src/softbus_adapter_standard.cpp", + ] + + include_dirs = [ + "//utils/native/base/include", + "../include/communicator", + "../include/dfx", + "../include/log", + "../include/autils", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//foundation/communication/dsoftbus/core/common/include", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "../dfx:distributeddata_dfx_static", + "//utils/native/base:utils", + ] + + external_deps = [ + "dsoftbus_standard:softbus_client", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + ] + + subsystem_name = "distributeddatamgr" +} diff --git a/services/distributeddataservice/adapter/communicator/src/app_device_handler.cpp b/services/distributeddataservice/adapter/communicator/src/app_device_handler.cpp new file mode 100755 index 000000000..a1054a664 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_device_handler.cpp @@ -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. + */ + +#include "app_device_handler.h" +#include "log_print.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "AppDeviceHandler" + +namespace OHOS { +namespace AppDistributedKv { +AppDeviceHandler::AppDeviceHandler() +{ + softbusAdapter_ = SoftBusAdapter::GetInstance(); +} + +AppDeviceHandler::~AppDeviceHandler() +{ + ZLOGI("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(); +} + +std::string AppDeviceHandler::GetUuidByNodeId(const std::string &nodeId) const +{ + return softbusAdapter_->GetUuidByNodeId(nodeId); +} + +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); +} + +void AppDeviceHandler::UpdateRelationship(const std::string &networkid, const DeviceChangeType &type) +{ + return softbusAdapter_->UpdateRelationship(networkid, type); +} + +std::string AppDeviceHandler::ToUUID(const std::string& id) const +{ + return softbusAdapter_->ToUUID(id); +} + +std::string AppDeviceHandler::ToNodeID(const std::string& id, const std::string &nodeId) const +{ + return softbusAdapter_->ToNodeID(id, nodeId); +} + +std::string AppDeviceHandler::ToBeAnonymous(const std::string &name) +{ + return SoftBusAdapter::ToBeAnonymous(name); +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/app_device_handler.h b/services/distributeddataservice/adapter/communicator/src/app_device_handler.h new file mode 100755 index 000000000..b1dbdef0d --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_device_handler.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 DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H +#define DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H +#include "softbus_adapter.h" + +namespace OHOS { +namespace AppDistributedKv { +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 GetUuidByNodeId(const std::string &nodeId) 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; + // transfer nodeId or udid to uuid + // input: id + // output: uuid + // return: transfer success or not + std::string ToUUID(const std::string &id) const; + // transfer uuid or udid to nodeId + // input: id + // output: nodeId + // return: transfer success or not + std::string ToNodeID(const std::string &id, const std::string &nodeId) const; + + static std::string ToBeAnonymous(const std::string &name); +private: + void UpdateRelationship(const std::string &networkid, const DeviceChangeType &type); + std::shared_ptr softbusAdapter_ {}; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAFWK_SRC_DEVICE_HANDLER_H diff --git a/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.cpp b/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.cpp new file mode 100755 index 000000000..7703672b5 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.cpp @@ -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. + */ + +#include "app_pipe_handler.h" +#include +#include +#include +#include +#include "log_print.h" +#include "reporter.h" +#include "dds_trace.h" + +#ifdef LOG_TAG + #undef LOG_TAG +#endif +#define LOG_TAG "AppPipeHandler" + +namespace OHOS { +namespace AppDistributedKv { +using namespace std; +using namespace OHOS::DistributedKv; + +AppPipeHandler::~AppPipeHandler() +{ + ZLOGI("destructor pipeId: %{public}s", pipeInfo_.pipeId.c_str()); +} + +AppPipeHandler::AppPipeHandler(const PipeInfo &pipeInfo) + : pipeInfo_(pipeInfo) +{ + ZLOGI("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); +} + +void AppPipeHandler::SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) +{ + return softbusAdapter_->SetMessageTransFlag(pipeInfo, flag); +} + +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 AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.h b/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.h new file mode 100755 index 000000000..b68509690 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_pipe_handler.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 DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H +#define DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H + +#include +#include +#include +#include +#include "app_types.h" +#include "kv_scheduler.h" +#include "log_print.h" +#include "reporter.h" +#include "app_data_change_listener.h" +#include "softbus_adapter.h" + +#ifdef LOG_TAG + #undef LOG_TAG +#endif +#define LOG_TAG "AppPipeHandler" + +namespace OHOS { +namespace AppDistributedKv { +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); + + void SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag); + + int CreateSessionServer(const std::string &sessionName) const; + + int RemoveSessionServer(const std::string &sessionName) const; +private: + PipeInfo pipeInfo_; + std::shared_ptr softbusAdapter_ {}; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#undef LOG_TAG +#endif /* DISTRIBUTEDDATAFWK_SRC_PIPE_HANDLER_H */ diff --git a/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.cpp b/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.cpp new file mode 100755 index 000000000..26fa9ea2d --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.cpp @@ -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. + */ + +#include "app_pipe_mgr.h" +#include "reporter.h" + +#undef LOG_TAG +#define LOG_TAG "AppPipeMgr" + +namespace OHOS { +namespace AppDistributedKv { +using namespace OHOS::DistributedKv; +Status AppPipeMgr::StartWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + ZLOGI("begin"); + if (observer == nullptr || pipeInfo.pipeId.empty()) { + ZLOGE("argument invalid"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + ZLOGE("pipeid not found"); + return Status::ERROR; + } + ZLOGI("end"); + return it->second->StartWatchDataChange(observer, pipeInfo); +} + +// stop DataChangeListener to watch data change; +Status AppPipeMgr::StopWatchDataChange(const AppDataChangeListener *observer, const PipeInfo &pipeInfo) +{ + ZLOGI("begin"); + if (observer == nullptr || pipeInfo.pipeId.empty()) { + ZLOGE("argument invalid"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + ZLOGE("pipeid not found"); + return Status::ERROR; + } + ZLOGI("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()) { + ZLOGW("Input is invalid, maxSize:%d, current size:%d", MAX_TRANSFER_SIZE, size); + return Status::ERROR; + } + ZLOGD("pipeInfo:%s ,size:%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()) { + ZLOGW("pipeInfo:%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()) { + ZLOGW("Start Failed, pipeInfo is empty."); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it != dataBusMap_.end()) { + ZLOGW("repeated start, pipeInfo:%{public}s.", pipeInfo.pipeId.c_str()); + return Status::REPEATED_REGISTER; + } + ZLOGD("Start pipeInfo:%{public}s ", pipeInfo.pipeId.c_str()); + auto handler = std::make_shared(pipeInfo); + int ret = handler->CreateSessionServer(pipeInfo.pipeId); + if (ret != 0) { + ZLOGW("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()) { + ZLOGW("pipeInfo:%s not found", pipeInfo.pipeId.c_str()); + return Status::KEY_NOT_FOUND; + } + appPipeHandler = it->second; + int ret = appPipeHandler->RemoveSessionServer(pipeInfo.pipeId); + if (ret != 0) { + ZLOGW("Stop pipeInfo:%s ret:%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) +{ + ZLOGI("start"); + if (pipeInfo.pipeId.empty() || peer.deviceId.empty()) { + ZLOGE("pipeId or deviceId is empty. Return false."); + return false; + } + ZLOGI("pipeInfo == [%s]", pipeInfo.pipeId.c_str()); + std::shared_ptr appPipeHandler; + { + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + ZLOGE("pipeInfo:%s not found. Return false.", pipeInfo.pipeId.c_str()); + return false; + } + appPipeHandler = it->second; + } + return appPipeHandler->IsSameStartedOnPeer(pipeInfo, peer); +} + +void AppPipeMgr::SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) +{ + if (pipeInfo.pipeId.empty()) { + return; + } + std::shared_ptr appPipeHandler; + { + std::lock_guard lock(dataBusMapMutex_); + auto it = dataBusMap_.find(pipeInfo.pipeId); + if (it == dataBusMap_.end()) { + ZLOGW("pipeInfo:%s not found", pipeInfo.pipeId.c_str()); + return; + } + appPipeHandler = it->second; + } + appPipeHandler->SetMessageTransFlag(pipeInfo, flag); +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.h b/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.h new file mode 100755 index 000000000..5bca39c88 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/app_pipe_mgr.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 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 "data_buffer.h" +#include "log_print.h" + +namespace OHOS { +namespace AppDistributedKv { +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); + + void SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag); +private: + std::mutex dataBusMapMutex_ {}; + std::map> dataBusMap_ {}; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_APP_PIPE_MGR_H diff --git a/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.cpp b/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.cpp new file mode 100755 index 000000000..b2ce805c5 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.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 "ark_communication_provider.h" +#include "log_print.h" + +#undef LOG_TAG +#define LOG_TAG "ArkCommunicationProvider" + +namespace OHOS { +namespace AppDistributedKv { +CommunicationProvider &ArkCommunicationProvider::Init() +{ + static ArkCommunicationProvider instance; + if (instance.isInited) { + return instance; + } + ZLOGI("begin"); + std::lock_guard lock(mutex_); + if (!instance.isInited) { + instance.Initialize(); + } + instance.isInited = true; + ZLOGI("normal end"); + return instance; +} +ArkCommunicationProvider::ArkCommunicationProvider() + : CommunicationProviderImpl(appPipeMgrImpl_, appDeviceHandlerImpl_) +{ +} + +DeviceInfo ArkCommunicationProvider::GetLocalDevice() const +{ + if (deviceQuery_ == nullptr) { + return CommunicationProviderImpl::GetLocalDevice(); + } + return deviceQuery_->GetLocalDevice(); +} + +std::vector ArkCommunicationProvider::GetDeviceList() const +{ + if (deviceQuery_ == nullptr) { + return CommunicationProviderImpl::GetDeviceList(); + } + return deviceQuery_->GetDeviceList(); +} + +void ArkCommunicationProvider::SetDeviceQuery(std::shared_ptr deviceQuery) +{ + ZLOGI("set device query"); + deviceQuery_ = deviceQuery; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.h b/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.h new file mode 100755 index 000000000..7c1e42802 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/ark_communication_provider.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 DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H +#define DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H + +#include "communication_provider_impl.h" +#include "idevice_query.h" +#include "app_device_handler.h" +#include "app_pipe_mgr.h" +#include "nocopyable.h" + +namespace OHOS { +namespace AppDistributedKv { +class ArkCommunicationProvider : public CommunicationProviderImpl { +public: + static CommunicationProvider &Init(); + void SetDeviceQuery(std::shared_ptr deviceQuery) override; + // Get online deviceList + std::vector GetDeviceList() const override; + // Get local device information + DeviceInfo GetLocalDevice() const override; + + ~ArkCommunicationProvider() override {}; +private: + DISALLOW_COPY_AND_MOVE(ArkCommunicationProvider); + ArkCommunicationProvider(); + std::shared_ptr deviceQuery_ {}; + AppPipeMgr appPipeMgrImpl_ {}; + AppDeviceHandler appDeviceHandlerImpl_ {}; + bool isInited = false; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAFWK_ARK_COMMUNICATION_PROVIDER_H diff --git a/services/distributeddataservice/adapter/communicator/src/communication_provider.cpp b/services/distributeddataservice/adapter/communicator/src/communication_provider.cpp new file mode 100644 index 000000000..50cb0a479 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/communication_provider.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 AppDistributedKv { +CommunicationProvider &CommunicationProvider::GetInstance() +{ + return ArkCommunicationProvider::Init(); +} + +std::shared_ptr CommunicationProvider::MakeCommunicationProvider() +{ + static std::shared_ptr instance(&ArkCommunicationProvider::Init(), [](void *) {}); + return instance; +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.cpp b/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.cpp new file mode 100755 index 000000000..259fdb2b0 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" + +#undef LOG_TAG +#define LOG_TAG "CommunicationProviderImpl" + +namespace OHOS { +namespace AppDistributedKv { +std::mutex CommunicationProviderImpl::mutex_; +CommunicationProviderImpl::CommunicationProviderImpl(AppPipeMgr &appPipeMgr, AppDeviceHandler &deviceHandler) + : appPipeMgr_(appPipeMgr), appDeviceHandler_(deviceHandler) +{ +} + +CommunicationProviderImpl::~CommunicationProviderImpl() +{ + ZLOGD("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); +} + +std::string CommunicationProviderImpl::GetUuidByNodeId(const std::string &nodeId) const +{ + return appDeviceHandler_.GetUuidByNodeId(nodeId); +} + +DeviceInfo CommunicationProviderImpl::GetLocalBasicInfo() const +{ + return appDeviceHandler_.GetLocalBasicInfo(); +} + +std::vector CommunicationProviderImpl::GetRemoteNodesBasicInfo() const +{ + return appDeviceHandler_.GetRemoteNodesBasicInfo(); +} + +std::string CommunicationProviderImpl::ToNodeId(const std::string &id) const +{ + std::string ret = appDeviceHandler_.ToNodeID(id, ""); + if (ret.empty()) { + ZLOGD("toNodeId failed."); + } + return ret; +} + +std::string CommunicationProviderImpl::GetUdidByNodeId(const std::string &nodeId) const +{ + return appDeviceHandler_.GetUdidByNodeId(nodeId); +} + +void CommunicationProviderImpl::SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) +{ + appPipeMgr_.SetMessageTransFlag(pipeInfo, flag); +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.h b/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.h new file mode 100755 index 000000000..4f1438334 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/communication_provider_impl.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 DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H +#define DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H + +#include "communication_provider.h" +#include +#include "app_device_handler.h" +#include "app_pipe_mgr.h" + +namespace OHOS { +namespace AppDistributedKv { +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; + std::string GetUuidByNodeId(const std::string &nodeId) const override; + std::string GetUdidByNodeId(const std::string &nodeId) const override; + DeviceInfo GetLocalBasicInfo() const override; + std::vector GetRemoteNodesBasicInfo() const override; + std::string ToNodeId(const std::string &id) const override; + void SetMessageTransFlag(const struct PipeInfo &pipeInfo, bool flag) override; +protected: + virtual Status Initialize(); + + static std::mutex mutex_; +private: + AppPipeMgr &appPipeMgr_; + AppDeviceHandler &appDeviceHandler_; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif /* DISTRIBUTEDDATA_SRC_COMMUNICATION_PROVIDER_IMPL_H */ diff --git a/services/distributeddataservice/adapter/communicator/src/data_buffer.cpp b/services/distributeddataservice/adapter/communicator/src/data_buffer.cpp new file mode 100755 index 000000000..13d89ada3 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/data_buffer.cpp @@ -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. + */ + +#include "data_buffer.h" + +namespace OHOS { +namespace AppDistributedKv { +DataBuffer::DataBuffer() : buf_(nullptr), size_(0), used_(0) +{} + +DataBuffer::~DataBuffer() +{ + if (buf_ != nullptr) { + delete[] buf_; + buf_ = nullptr; + } +} + +bool DataBuffer::Init(size_t size) +{ + if (buf_ != nullptr) { + return false; + } + size_ = std::min(size, MAX_DATA_LEN); + buf_ = new(std::nothrow) char[size_](); + if (buf_ == nullptr) { + return false; + } + used_ = 0; + return true; +} + +const char *DataBuffer::GetBufPtr() const +{ + return buf_; +} + +size_t DataBuffer::GetBufSize() const +{ + return size_; +} + +void DataBuffer::SetBufUsed(size_t used) +{ + used_ = used; +} + +size_t DataBuffer::GetBufUsed() const +{ + return used_; +} +} // namespace AppDistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/communicator/src/data_buffer.h b/services/distributeddataservice/adapter/communicator/src/data_buffer.h new file mode 100644 index 000000000..1503036fa --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/data_buffer.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 DISTRIBUTEDDATAMGR_DATA_BUFFER_H +#define DISTRIBUTEDDATAMGR_DATA_BUFFER_H + +#include +#include + +namespace OHOS { +namespace AppDistributedKv { +struct HeaderInfo { + uint32_t type; + uint32_t length; + uint32_t version; + uint32_t sequence; +} __attribute__((packed)); + +// header length +static const int HEADER_LEN = sizeof(HeaderInfo); + +// max size for transferring data using pipe is 5M +static const size_t MAX_DATA_LEN = 1024 * 1024 * 5; + +// 5M; max size for transfer using pipe (12 is header length, rest is data wait for transferring) +static const int MAX_TRANSFER_SIZE = 1024 * 1024 * 5 - HEADER_LEN; + +static const uint32_t VERSION = 0; + +static const uint32_t TYPE = 0; + +static int g_sequence = 0; + +union Head { + HeaderInfo headerInfo; + uint8_t headArray[HEADER_LEN]; +} __attribute__((packed)); + +class DataBuffer { +public: + DataBuffer(); + + ~DataBuffer(); + + bool Init(size_t size); + + const char *GetBufPtr() const; + + size_t GetBufSize() const; + + void SetBufUsed(size_t used); + + size_t GetBufUsed() const; +private: + char *buf_; + size_t size_; + size_t used_; +}; +} +} +#endif // DISTRIBUTEDDATAMGR_DATA_BUFFER_H diff --git a/services/distributeddataservice/adapter/communicator/src/process_communicator_impl.cpp b/services/distributeddataservice/adapter/communicator/src/process_communicator_impl.cpp new file mode 100755 index 000000000..797a27f76 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/process_communicator_impl.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "processCommunication" + +#include "process_communicator_impl.h" + +#include "log_print.h" + +namespace OHOS { +namespace AppDistributedKv { +using namespace DistributedDB; +ProcessCommunicatorImpl::ProcessCommunicatorImpl() +{ +} + +ProcessCommunicatorImpl::~ProcessCommunicatorImpl() +{ + ZLOGE("destructor."); +} + +DBStatus ProcessCommunicatorImpl::Start(const std::string &processLabel) +{ + ZLOGI("init commProvider"); + thisProcessLabel_ = processLabel; + PipeInfo pi = {thisProcessLabel_, ""}; + Status errCode = CommunicationProvider::GetInstance().Start(pi); + if (errCode != Status::SUCCESS) { + ZLOGE("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) { + ZLOGE("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) { + ZLOGE("commProvider_ StartWatchDeviceChange Fail."); + return DBStatus::DB_ERROR; + } + } else { + Status errCode = CommunicationProvider::GetInstance().StopWatchDeviceChange(this, pi); + if (errCode != Status::SUCCESS) { + ZLOGE("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) { + ZLOGE("commProvider_ StartWatchDataChange Fail."); + return DBStatus::DB_ERROR; + } + } else { + Status errCode = CommunicationProvider::GetInstance().StopWatchDataChange(this, pi); + if (errCode != Status::SUCCESS) { + ZLOGE("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) { + ZLOGE("commProvider_ SendData Fail."); + return DBStatus::DB_ERROR; + } + + return DBStatus::OK; +} + +uint32_t ProcessCommunicatorImpl::GetMtuSize() +{ + return MTU_SIZE; +} + +uint32_t ProcessCommunicatorImpl::GetMtuSize(const DeviceInfos &devInfo) +{ + ZLOGI("GetMtuSize start"); + std::vector devInfos = CommunicationProvider::GetInstance().GetDeviceList(); + for (auto const &entry : devInfos) { + ZLOGI("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) { + ZLOGE("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) { + ZLOGE("onDeviceChangeHandler_ invalid."); + return; + } + DeviceInfos devInfo; + devInfo.identifier = info.deviceId; + onDeviceChangeHandler_(devInfo, (type == DeviceChangeType::DEVICE_ONLINE)); +} +} // namespace AppDistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/communicator/src/softbus_adapter.h b/services/distributeddataservice/adapter/communicator/src/softbus_adapter.h new file mode 100755 index 000000000..fe904a84e --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/softbus_adapter.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#include "app_data_change_listener.h" +#include "app_device_status_change_listener.h" +#include "app_types.h" +#include "platform_specific.h" + +namespace OHOS { +namespace AppDistributedKv { +enum IdType { + NETWORKID, + UUID, + UDID, +}; +class SoftBusAdapter { +public: + SoftBusAdapter(); + ~SoftBusAdapter(); + static std::shared_ptr GetInstance(); + + void Init() const; + // 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 GetUuidByNodeId(const std::string &nodeId) 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; + // transfer nodeId or udid to uuid + // input: id + // output: uuid + // return: transfer success or not + std::string ToUUID(const std::string &id) const; + // transfer uuid or udid to nodeId + // input: id + // output: nodeId + // return: transfer success or not + std::string ToNodeID(const std::string &id, const std::string &nodeId) 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) const; + + 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 std::string &ptr, const int size, const std::string &deviceId, + const PipeInfo &pipeInfo); +private: + std::map> networkId2UuidUdid_ {}; + 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 +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif /* DISTRIBUTEDDATAFWK_SRC_SOFTBUS_ADAPTER_H */ \ No newline at end of file diff --git a/services/distributeddataservice/adapter/communicator/src/softbus_adapter_standard.cpp b/services/distributeddataservice/adapter/communicator/src/softbus_adapter_standard.cpp new file mode 100755 index 000000000..11284938c --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/src/softbus_adapter_standard.cpp @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "softbus_adapter.h" + +#include +#include +#include "dds_trace.h" +#include "dfx_types.h" +#include "kv_store_delegate_manager.h" +#include "log_print.h" +#include "process_communicator_impl.h" +#include "reporter.h" +#include "session.h" +#include "softbus_bus_center.h" +#include "softbus_def.h" +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "SoftBusAdapter" + +namespace OHOS { +namespace AppDistributedKv { +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; +using namespace std; +using namespace OHOS::DistributedKv; + +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 std::string &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) +{ + ZLOGI("device change listener info change NodeBasicInfoType:%d, networkId:%s, deviceName:%s, deviceTypeId:%d.", + type, info->networkId, info->deviceName, info->deviceTypeId); +} + +void AppDeviceListenerWrap::OnDeviceOffline(NodeBasicInfo *info) +{ + ZLOGI("device change listener offline."); + NotifyAll(info, DeviceChangeType::DEVICE_OFFLINE); +} + +void AppDeviceListenerWrap::OnDeviceOnline(NodeBasicInfo *info) +{ + ZLOGI("device change listener online."); + NotifyAll(info, DeviceChangeType::DEVICE_ONLINE); +} + +void AppDeviceListenerWrap::SetDeviceHandler(SoftBusAdapter *handler) +{ + ZLOGI("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() +{ + AppDeviceListenerWrap::SetDeviceHandler(this); + AppDataListenerWrap::SetDataHandler(this); +} + +SoftBusAdapter::~SoftBusAdapter() +{ + INodeStateCb iNodeStateCb; + iNodeStateCb.events = EVENT_NODE_STATE_MASK; + iNodeStateCb.onNodeOnline = AppDeviceListenerWrap::OnDeviceOnline; + iNodeStateCb.onNodeOffline = AppDeviceListenerWrap::OnDeviceOffline; + iNodeStateCb.onNodeBasicInfoChanged = AppDeviceListenerWrap::OnDeviceInfoChanged; + int32_t errNo = UnregNodeDeviceStateCb(&iNodeStateCb); + if (errNo != SOFTBUS_OK) { + ZLOGE("UnregNodeDeviceStateCb fail %{public}d", errNo); + } +} + +void SoftBusAdapter::Init() const +{ + std::thread th = std::thread([&]() { + auto communicator = std::make_shared(); + auto retcom = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + ZLOGI("set communicator ret:%{public}d.", static_cast(retcom)); + + INodeStateCb iNodeStateCb; + iNodeStateCb.events = EVENT_NODE_STATE_MASK; + iNodeStateCb.onNodeOnline = AppDeviceListenerWrap::OnDeviceOnline; + iNodeStateCb.onNodeOffline = AppDeviceListenerWrap::OnDeviceOffline; + iNodeStateCb.onNodeBasicInfoChanged = AppDeviceListenerWrap::OnDeviceInfoChanged; + + int i = 0; + constexpr int RETRY_TIMES = 300; + while (i++ < RETRY_TIMES) { + int32_t errNo = RegNodeDeviceStateCb("com.huawei.hwddmp", &iNodeStateCb); + if (errNo != SOFTBUS_OK) { + ZLOGE("RegNodeDeviceStateCb fail %{public}d, time:%{public}d", errNo, i); + std::this_thread::sleep_for(std::chrono::seconds(1)); + continue; + } + return; + } + ZLOGE("AppDeviceHandler Init failed %{public}d times and exit now.", RETRY_TIMES); + }); + th.detach(); +} + +Status SoftBusAdapter::StartWatchDeviceChange(const AppDeviceStatusChangeListener *observer, + __attribute__((unused)) const PipeInfo &pipeInfo) +{ + ZLOGI("begin"); + if (observer == nullptr) { + ZLOGW("observer is null."); + return Status::ERROR; + } + std::lock_guard lock(deviceChangeMutex_); + auto result = listeners_.insert(observer); + if (!result.second) { + ZLOGW("Add listener error."); + return Status::ERROR; + } + ZLOGI("end"); + return Status::SUCCESS; +} + +Status SoftBusAdapter::StopWatchDeviceChange(const AppDeviceStatusChangeListener *observer, + __attribute__((unused)) const PipeInfo &pipeInfo) +{ + ZLOGI("begin"); + if (observer == nullptr) { + ZLOGW("observer is null."); + return Status::ERROR; + } + std::lock_guard lock(deviceChangeMutex_); + auto result = listeners_.erase(observer); + if (result <= 0) { + return Status::ERROR; + } + ZLOGI("end"); + return Status::SUCCESS; +} + +void SoftBusAdapter::NotifyAll(const DeviceInfo &deviceInfo, const DeviceChangeType &type) +{ + std::thread th = std::thread([this, deviceInfo, type]() { + std::lock_guard lock(deviceChangeMutex_); + ZLOGD("notify id:%{public}s", ToBeAnonymous(deviceInfo.deviceId).c_str()); + ZLOGD("high"); + std::string uuid = GetUuidByNodeId(deviceInfo.deviceId); + UpdateRelationship(deviceInfo.deviceId, type); + for (const auto &device : listeners_) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::HIGH) { + DeviceInfo di = {uuid, deviceInfo.deviceName, deviceInfo.deviceType}; + device->OnDeviceChanged(di, type); + break; + } + } + ZLOGD("low"); + for (const auto &device : listeners_) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::LOW) { + DeviceInfo di = {uuid, deviceInfo.deviceName, deviceInfo.deviceType}; + device->OnDeviceChanged(di, DeviceChangeType::DEVICE_OFFLINE); + device->OnDeviceChanged(di, type); + } + } + ZLOGD("min"); + for (const auto &device : listeners_) { + if (device == nullptr) { + continue; + } + if (device->GetChangeLevelType() == ChangeLevelType::MIN) { + DeviceInfo di = {uuid, 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("com.huawei.hwddmp", &info, &infoNum); + if (ret != SOFTBUS_OK) { + ZLOGE("GetAllNodeDeviceInfo error"); + return dis; + } + ZLOGD("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)}); + } + return dis; +} + +DeviceInfo SoftBusAdapter::GetLocalDevice() +{ + if (!localInfo_.deviceId.empty()) { + return localInfo_; + } + + NodeBasicInfo info; + int32_t ret = GetLocalNodeDeviceInfo("com.huawei.hwddmp", &info); + if (ret != SOFTBUS_OK) { + ZLOGE("GetLocalNodeDeviceInfo error"); + return DeviceInfo(); + } + ZLOGD("Udid:%{private}s, name:%{private}s, type:%{private}d", + ToBeAnonymous(std::string(info.networkId)).c_str(), info.deviceName, info.deviceTypeId); + localInfo_ = {std::string(info.networkId), std::string(info.deviceName), std::to_string(info.deviceTypeId)}; + return localInfo_; +} + +std::string SoftBusAdapter::GetUuidByNodeId(const std::string &nodeId) const +{ + uint8_t info = 0; + int32_t ret = GetNodeKeyInfo("com.huawei.hwddmp", nodeId.c_str(), + NodeDeivceInfoKey::NODE_KEY_UUID, &info, sizeof(info)); + if (ret != SOFTBUS_OK) { + ZLOGW("GetNodeKeyInfo error"); + return ""; + } + ZLOGD("getUuid:%{public}s", nodeId.c_str()); + return std::to_string(info); +} + +std::string SoftBusAdapter::GetUdidByNodeId(const std::string &nodeId) const +{ + uint8_t info = 0; + int32_t ret = GetNodeKeyInfo("com.huawei.hwddmp", nodeId.c_str(), + NodeDeivceInfoKey::NODE_KEY_UDID, &info, sizeof(info)); + if (ret != SOFTBUS_OK) { + ZLOGW("GetNodeKeyInfo error"); + return ""; + } + ZLOGD("getUdid:%{public}s", nodeId.c_str()); + return std::to_string(info); +} + +DeviceInfo SoftBusAdapter::GetLocalBasicInfo() const +{ + ZLOGD("SoftBusAdapter::GetLocalBasicInfo begin"); + NodeBasicInfo info; + int32_t ret = GetLocalNodeDeviceInfo("com.huawei.hwddmp", &info); + if (ret != SOFTBUS_OK) { + ZLOGE("GetLocalNodeDeviceInfo error"); + return DeviceInfo(); + } + ZLOGD("Udid:%{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 +{ + std::vector ret; + return ret; +} + +void SoftBusAdapter::UpdateRelationship(const std::string &networkid, const DeviceChangeType &type) +{ + auto uuid = GetUuidByNodeId(networkid); + auto udid = GetUdidByNodeId(networkid); + switch (type) { + case DeviceChangeType::DEVICE_OFFLINE: { + auto size = this->networkId2UuidUdid_.erase(networkid); + if (size == 0) { + ZLOGW("not found id:%{public}s.", networkid.c_str()); + } + break; + } + case DeviceChangeType::DEVICE_ONLINE: { + std::pair> value = {networkid, {uuid, udid}}; + auto res = this->networkId2UuidUdid_.insert(std::move(value)); + if (!res.second) { + ZLOGW("insert failed."); + } + break; + } + default: { + ZLOGW("unknown type."); + break; + } + } +} + +std::string SoftBusAdapter::ToUUID(const std::string& id) const +{ + auto res = networkId2UuidUdid_.find(id); + if (res != networkId2UuidUdid_.end()) { // id is networkid + return std::get<0>(res->second); + } + + for (auto const &e : networkId2UuidUdid_) { + auto tup = e.second; + if (id == (std::get<0>(tup))) { // id is uuid + return id; + } + if (id == (std::get<1>(tup))) { // id is udid + return std::get<0>(tup); + } + } + ZLOGW("unknown id."); + return ""; +} + +std::string SoftBusAdapter::ToNodeID(const std::string& id, const std::string &nodeId) const +{ + for (auto const &e : networkId2UuidUdid_) { + auto tup = e.second; + if (nodeId == (std::get<0>(tup))) { // id is uuid + return e.first; + } + if (id == (std::get<1>(tup))) { // id is udid + return e.first; + } + } + return ""; +} + +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) +{ + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + lock_guard lock(dataChangeMutex_); + auto it = dataChangeListeners_.find(pipeInfo.pipeId); + if (it != dataChangeListeners_.end()) { + ZLOGW("Add listener error or repeated adding."); + return Status::ERROR; + } + ZLOGD("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) +{ + lock_guard lock(dataChangeMutex_); + if (dataChangeListeners_.erase(pipeInfo.pipeId)) { + return Status::SUCCESS; + } + ZLOGW("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; + int sessionId = OpenSession(pipeInfo.pipeId.c_str(), pipeInfo.pipeId.c_str(), + deviceId.deviceId.c_str(), pipeInfo.userId.c_str(), &attr); + if (sessionId == INVALID_SESSION_ID) { + ZLOGW("create session %{public}s, %{public}d failed, session is null.", pipeInfo.pipeId.c_str(), info.msgType); + return Status::CREATE_SESSION_ERROR; + } + + ZLOGD("SendBytes start,size is %{public}d, session type is %{public}d.", size, attr.dataType); + int32_t ret = SendBytes(sessionId, (void*)ptr, size); + if (ret != SOFTBUS_OK) { + ZLOGW("send data %{public}s failed, session is null.", pipeInfo.pipeId.c_str()); + return Status::ERROR; + } + return Status::SUCCESS; +} + +bool SoftBusAdapter::IsSameStartedOnPeer(const struct PipeInfo &pipeInfo, + __attribute__((unused))const struct DeviceId &peer) +{ + ZLOGI("pipeInfo:%{public}s", pipeInfo.pipeId.c_str()); + { + lock_guard lock(busSessionMutex_); + if (busSessionMap_.find(pipeInfo.pipeId + peer.deviceId) != busSessionMap_.end()) { + ZLOGI("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(), peer.deviceId.c_str(), + pipeInfo.userId.c_str(), &attr); + if (sessionId == INVALID_SESSION_ID) { + ZLOGE("OpenSession return null, pipeInfo:%{public}s. Return false.", pipeInfo.pipeId.c_str()); + return false; + } + ZLOGI("session started, pipeInfo:%{public}s. Return true.", pipeInfo.pipeId.c_str()); + return true; +} + +void SoftBusAdapter::SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) +{ + ZLOGI("SetMessageTransFlag pipeInfo: %s flag: %d", pipeInfo.pipeId.c_str(), static_cast(flag)); + flag_ = flag; +} + +int SoftBusAdapter::CreateSessionServerAdapter(const std::string &sessionName) const +{ + ISessionListener sessionListener; + sessionListener.OnSessionOpened = AppDataListenerWrap::OnSessionOpened; + sessionListener.OnSessionClosed = AppDataListenerWrap::OnSessionClosed; + sessionListener.OnBytesReceived = AppDataListenerWrap::OnBytesReceived; + sessionListener.OnMessageReceived = AppDataListenerWrap::OnMessageReceived; + int ret = CreateSessionServer("com.huawei.hwddmp", sessionName.c_str(), &sessionListener); + if (ret != SOFTBUS_OK) { + ZLOGW("Start pipeInfo:%{public}s, failed ret:%{public}d.", sessionName.c_str(), ret); + return ret; + } + return SOFTBUS_OK; +} + +int SoftBusAdapter::RemoveSessionServerAdapter(const std::string &sessionName) const +{ + return RemoveSessionServer("com.huawei.hwddmp", 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 std::string &ptr, const int size, const std::string &deviceId, + const PipeInfo &pipeInfo) +{ + lock_guard lock(dataChangeMutex_); + auto it = dataChangeListeners_.find(pipeInfo.pipeId); + if (it != dataChangeListeners_.end()) { + ZLOGD("ready to notify, pipeName:%{public}s.", pipeInfo.pipeId.c_str()); + it->second->OnMessage({deviceId, "", ""}, (uint8_t *)ptr.c_str(), size, pipeInfo); + TrafficStat ts { pipeInfo.pipeId, deviceId, 0, size }; + Reporter::GetInstance()->TrafficStatistic()->Report(ts); + return; + } + ZLOGW("no listener %{public}s.", pipeInfo.pipeId.c_str()); +} + +void AppDataListenerWrap::SetDataHandler(SoftBusAdapter *handler) +{ + ZLOGI("SetDeviceHandler."); + softBusAdapter_ = handler; +} + +int AppDataListenerWrap::OnSessionOpened(int sessionId, int result) +{ + char mySessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + + if (result != SOFTBUS_OK) { + ZLOGW("open session %{public}d failed, result id is %{public}d.", sessionId, result); + return result; + } + + int ret = GetMySessionName(sessionId, mySessionName, sizeof(mySessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my session name failed, session id is %{public}d.", sessionId); + return SOFTBUS_ERR; + } + ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("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) { + ZLOGW("get my peer device id failed, session id is %{public}d.", sessionId); + return SOFTBUS_ERR; + } + ZLOGW("%{public}s, %{public}s", mySessionName, peerSessionName); + + if (strlen(peerSessionName) < 1) { + softBusAdapter_->InsertSession(std::string(mySessionName) + std::string(peerDevId)); + } else { + softBusAdapter_->InsertSession(std::string(peerSessionName) + std::string(peerDevId)); + } + return 0; +} + +void AppDataListenerWrap::OnSessionClosed(int sessionId) +{ + char mySessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + + int ret = GetMySessionName(sessionId, mySessionName, sizeof(mySessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + ZLOGW("%{public}s, %{public}s", mySessionName, peerSessionName); + + if (strlen(peerSessionName) < 1) { + softBusAdapter_->DeleteSession(std::string(mySessionName) + std::string(peerDevId)); + } else { + softBusAdapter_->DeleteSession(std::string(peerSessionName) + std::string(peerDevId)); + } +} + +void AppDataListenerWrap::OnMessageReceived(int sessionId, const void *data, unsigned int dataLen) +{ + if (sessionId == INVALID_SESSION_ID) { + return; + } + + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + std::string dataStr = *static_cast(data); + int ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + NotifyDataListeners(dataStr, dataLen, std::string(peerDevId), {std::string(peerSessionName), ""}); +} + +void AppDataListenerWrap::OnBytesReceived(int sessionId, const void *data, unsigned int dataLen) +{ + if (sessionId == INVALID_SESSION_ID) { + return; + } + + char peerSessionName[SESSION_NAME_SIZE_MAX] = ""; + char peerDevId[DEVICE_ID_SIZE_MAX] = ""; + std::string dataStr = *static_cast(data); + int ret = GetPeerSessionName(sessionId, peerSessionName, sizeof(peerSessionName)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer session name failed, session id is %{public}d.", sessionId); + return; + } + ret = GetPeerDeviceId(sessionId, peerDevId, sizeof(peerDevId)); + if (ret != SOFTBUS_OK) { + ZLOGW("get my peer device id failed, session id is %{public}d.", sessionId); + return; + } + NotifyDataListeners(dataStr, dataLen, std::string(peerDevId), {std::string(peerSessionName), ""}); +} + +void AppDataListenerWrap::NotifyDataListeners(const std::string &ptr, const int size, const string &deviceId, + const PipeInfo &pipeInfo) +{ + return softBusAdapter_->NotifyDataListeners(ptr, size, deviceId, pipeInfo); +} +} // namespace AppDistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/communicator/test/BUILD.gn b/services/distributeddataservice/adapter/communicator/test/BUILD.gn new file mode 100755 index 000000000..919d22167 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/test/BUILD.gn @@ -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. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddatafwk" + +############################################################################### +ohos_unittest("CommunicationProviderTest") { + module_out_path = module_output_path + + sources = [ "./unittest/communication_provider_impl_test.cpp" ] + include_dirs = [ + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/log", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/autils", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/communicator", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/dfx", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "../src", + ] + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "dsoftbus_standard:softbus_client", + ] + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/communicator:distributeddata_communicator_static", + "//third_party/googletest:gtest_main", + ] +} + +############################################################################### +config("module_comm_config") { + visibility = [ ":*" ] + + include_dirs = [ + "./unittest/communicator", + "./unittest/communicator/include", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/communicator", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/dfx", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/log", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/autils", + ] +} + +############################################################################### +group("unittest") { + testonly = true + + deps = [] + + deps += [ + ":CommunicationProviderTest", + ] +} +############################################################################### diff --git a/services/distributeddataservice/adapter/communicator/test/unittest/communication_provider_impl_test.cpp b/services/distributeddataservice/adapter/communicator/test/unittest/communication_provider_impl_test.cpp new file mode 100755 index 000000000..175ea4c75 --- /dev/null +++ b/services/distributeddataservice/adapter/communicator/test/unittest/communication_provider_impl_test.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. + */ + +#define LOG_TAG "CommunicationProviderImplTest" + +#include +#include +#include +#include +#include +#include "app_types.h" +#include "app_device_status_change_listener.h" +#include "communication_provider.h" +#include "log_print.h" +#include "app_device_handler.h" + +using namespace std; +using namespace testing::ext; +using namespace OHOS::AppDistributedKv; +class CommunicationProviderImplTest : public testing::Test { +}; + +class AppDataChangeListenerImpl : public AppDataChangeListener { + void OnMessage(const DeviceInfo &info, const uint8_t *ptr, const int size, + const struct PipeInfo &id) const override; +}; +void AppDataChangeListenerImpl::OnMessage(const DeviceInfo &info, const uint8_t *ptr, const int size, + const struct PipeInfo &id) const +{ + ZLOGI("data %{public}s %s", info.deviceName.c_str(), ptr); +} + +class AppDeviceStatusChangeListenerImpl : public AppDeviceStatusChangeListener { +public: + void OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const override; + ~AppDeviceStatusChangeListenerImpl(); +}; + +void AppDeviceStatusChangeListenerImpl::OnDeviceChanged(const DeviceInfo &info, const DeviceChangeType &type) const +{ + ZLOGI("%{public}s %{public}d", info.deviceName.c_str(), static_cast(type)); +} + +AppDeviceStatusChangeListenerImpl::~AppDeviceStatusChangeListenerImpl() +{ +} + +/** +* @tc.name: CommunicationProvider003 +* @tc.desc: Verify getting KvStore +* @tc.type: FUNC +* @tc.require: AR000CCPQ1 AR000CQDVE +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider003, TestSize.Level1) +{ + ZLOGI("begin."); + const AppDataChangeListenerImpl* dataListener = new AppDataChangeListenerImpl(); + PipeInfo appId; + appId.pipeId = "appId"; + appId.userId = "groupId"; + CommunicationProvider::GetInstance().StartWatchDataChange(dataListener, appId); + auto secRegister = CommunicationProvider::GetInstance().StartWatchDataChange(dataListener, appId); + EXPECT_EQ(Status::ERROR, secRegister); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider004 +* @tc.desc: Verify stop watch device change +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 AR000CQS3F +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider004, TestSize.Level1) +{ + ZLOGD("CommunicationProvider004"); + const AppDataChangeListenerImpl* dataListener = new AppDataChangeListenerImpl(); + PipeInfo appId; + appId.pipeId = "appId"; + appId.userId = "groupId"; + auto secRegister = CommunicationProvider::GetInstance().StopWatchDataChange(dataListener, appId); + ZLOGD("CommunicationProvider004 %d", static_cast(secRegister)); + EXPECT_EQ(Status::ERROR, secRegister); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider005 +* @tc.desc: Verify stop watch device change +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 AR000CQS3G AR000CQS3F +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider005, TestSize.Level1) +{ + ZLOGD("CommunicationProvider005"); + const AppDeviceStatusChangeListenerImpl* dataListener = new AppDeviceStatusChangeListenerImpl(); + PipeInfo appId; + appId.pipeId = "appId"; + appId.userId = "groupId"; + auto firRegister = CommunicationProvider::GetInstance().StartWatchDeviceChange(dataListener, appId); + + EXPECT_EQ(Status::SUCCESS, firRegister); + auto secStop = CommunicationProvider::GetInstance().StopWatchDeviceChange(dataListener, appId); + EXPECT_EQ(Status::SUCCESS, secStop); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider006 +* @tc.desc: Verify stop watch device change +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider006, TestSize.Level1) +{ + ZLOGI("GetDeviceList"); + auto devices = CommunicationProvider::GetInstance().GetDeviceList(); + const unsigned long val = 0; + ZLOGD("GetDeviceList size: %zu", devices.size()); + ASSERT_GE(devices.size(), val); + for (const auto &device : devices) { + ZLOGD("GetDeviceList id: %s, name:%s, type:%s", + AppDeviceHandler::ToBeAnonymous(device.deviceId).c_str(), + device.deviceName.c_str(), device.deviceType.c_str()); + } + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider007 +* @tc.desc: Verify Local deviceId exist +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 AR000CQS3I +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider007, TestSize.Level1) +{ + ZLOGI("CommunicationProvider007 GetLocalDevice"); + auto device = CommunicationProvider::GetInstance().GetLocalDevice(); + const unsigned long val = 0; + ASSERT_GE(device.deviceName.length(), val); + ZLOGD("GetLocalDevice id: %s, name:%s, type:%s", + AppDeviceHandler::ToBeAnonymous(device.deviceId).c_str(), + device.deviceName.c_str(), device.deviceType.c_str()); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider015 +* @tc.desc: close pipe +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider015, TestSize.Level1) +{ + ZLOGI("CommunicationProvider015 "); + PipeInfo appId; + appId.pipeId = "appId"; + appId.userId = "groupId"; + auto status = CommunicationProvider::GetInstance().Stop(appId); + EXPECT_NE(Status::ERROR, status); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider016 +* @tc.desc: singleton pipe +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider016, TestSize.Level1) +{ + ZLOGI("begin."); + auto &provider = CommunicationProvider::GetInstance(); + auto &provider1 = CommunicationProvider::GetInstance(); + EXPECT_EQ(&provider, &provider1); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider017 +* @tc.desc: parse sent data +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 AR000CQS3M AR000CQSAI +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider017, TestSize.Level1) +{ + const AppDataChangeListenerImpl *dataListener17 = new AppDataChangeListenerImpl(); + PipeInfo id17; + id17.pipeId = "appId"; + id17.userId = "groupId"; + CommunicationProvider::GetInstance().StartWatchDataChange(dataListener17, id17); + CommunicationProvider::GetInstance().Start(id17); + std::string content = "Helloworlds"; + const uint8_t *t = (uint8_t *)(content.c_str()); + DeviceId di17 = {"127.0.0.2"}; + Status status = CommunicationProvider::GetInstance().SendData(id17, di17, t, content.length()); + EXPECT_NE(status, Status::SUCCESS); + CommunicationProvider::GetInstance().StopWatchDataChange(dataListener17, id17); + CommunicationProvider::GetInstance().Stop(id17); + delete dataListener17; + sleep(1); // avoid thread dnet thread died, then will have pthread; +} + +/** +* @tc.name: CommunicationProvider018 +* @tc.desc: test isPeerAvailable +* @tc.type: FUNC +* @tc.require: AR000CCPQ2 AR000CQS3C AR000CQS3D SR000CQS3B AR000CQSAI +* @tc.author: hongbo +*/ +HWTEST_F(CommunicationProviderImplTest, CommunicationProvider018, TestSize.Level1) +{ + struct PipeInfo appId; + appId.pipeId = "appIda"; + appId.userId = "groupIds"; + struct DeviceId di; + di.deviceId = "mydeviceid0"; + bool ret = CommunicationProvider::GetInstance().IsSameStartedOnPeer(appId, di); + EXPECT_EQ(true, ret); + sleep(1); // avoid thread dnet thread died, then will have pthread; +} diff --git a/services/distributeddataservice/adapter/dfx/BUILD.gn b/services/distributeddataservice/adapter/dfx/BUILD.gn new file mode 100755 index 000000000..16dc7d444 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_dfx_static") { + sources = [ + "src/dds_trace.cpp", + "src/fault/communication_fault_impl.cpp", + "src/fault/database_fault_impl.cpp", + "src/fault/runtime_fault_impl.cpp", + "src/fault/service_fault_impl.cpp", + "src/hiview_adapter.cpp", + "src/reporter.cpp", + "src/statistic/api_performance_statistic_impl.cpp", + "src/statistic/database_statistic_impl.cpp", + "src/statistic/traffic_statistic_impl.cpp", + "src/statistic/visit_statistic_impl.cpp", + ] + + include_dirs = [ + "./src", + "./src/fault", + "./src/statistic", + "../include/dfx", + "../include/log", + "../include/autils", + "//utils/native/base/include", + "//third_party/openssl/include/", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ + "../autils:distributeddata_autils_static", + "//third_party/openssl:libcrypto_static", + "//utils/native/base:utils", + ] + external_deps = [ + "bytrace_standard:bytrace_core", + "hisysevent_native:libhisysevent", + "hiviewdfx_hilog_native:libhilog", + ] +} diff --git a/services/distributeddataservice/adapter/dfx/src/dds_trace.cpp b/services/distributeddataservice/adapter/dfx/src/dds_trace.cpp new file mode 100755 index 000000000..cf61e88b7 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/dds_trace.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DdsTrace" + +#include "dds_trace.h" +#include +#include +#include "bytrace.h" +#include "log_print.h" +#include "time_utils.h" +#include "reporter.h" + +namespace OHOS { +namespace DistributedKv { +static constexpr uint64_t BYTRACE_LABEL = BYTRACE_TAG_DISTRIBUTEDDATA; + +std::atomic_uint DdsTrace::switchOption = DdsTrace::API_PERFORMANCE_TRACE_ON; +std::atomic_uint DdsTrace::indexCount = 0; // the value is changed by different thread +std::atomic_bool DdsTrace::isSetBytraceEnabled = false; + +DdsTrace::DdsTrace(const std::string& value, bool isSend) +{ + traceValue = value; + traceCount = ++indexCount; + isSendToHiview = isSend; + SetBytraceEnable(); + + Start(value); +} + +DdsTrace::~DdsTrace() +{ + Finish(traceValue); +} + +void DdsTrace::SetMiddleTrace(const std::string& beforeValue, const std::string& afterValue) +{ + traceValue = afterValue; + Middle(beforeValue, afterValue); +} + +void DdsTrace::Start(const std::string& value) +{ + if (switchOption == DEBUG_CLOSE) { + return; + } + if ((switchOption & BYTRACE_ON) == BYTRACE_ON) { + StartTrace(BYTRACE_LABEL, value); + } + if ((switchOption & API_PERFORMANCE_TRACE_ON) == API_PERFORMANCE_TRACE_ON) { + lastTime = TimeUtils::CurrentTimeMicros(); + } + ZLOGD("DdsTrace-Start: Trace[%{public}u] %{public}s In", traceCount, value.c_str()); +} + +void DdsTrace::Middle(const std::string& beforeValue, const std::string& afterValue) +{ + if (switchOption == DEBUG_CLOSE) { + return; + } + if (switchOption & BYTRACE_ON) { + MiddleTrace(BYTRACE_LABEL, beforeValue, afterValue); + } + ZLOGD("DdsTrace-Middle: Trace[%{public}u] %{public}s --- %{public}s", traceCount, + beforeValue.c_str(), afterValue.c_str()); +} + +void DdsTrace::Finish(const std::string& value) +{ + uint64_t delta = 0; + if (switchOption == DEBUG_CLOSE) { + return; + } + if (switchOption & BYTRACE_ON) { + FinishTrace(BYTRACE_LABEL, value); + } + if (switchOption & API_PERFORMANCE_TRACE_ON) { + delta = TimeUtils::CurrentTimeMicros() - lastTime; + if (isSendToHiview) { + Reporter::GetInstance()->ApiPerformanceStatistic()->Report({value, delta, delta, delta}); + } + } + ZLOGD("DdsTrace-Finish: Trace[%u] %{public}s Out: %{public}" PRIu64"us.", traceCount, value.c_str(), delta); +} + +bool DdsTrace::SetBytraceEnable() +{ + if (isSetBytraceEnabled) { + return true; + } + + UpdateTraceLabel(); + isSetBytraceEnabled = true; + DdsTrace::switchOption = DdsTrace::BYTRACE_ON | DdsTrace::API_PERFORMANCE_TRACE_ON; + + ZLOGD("success, current tag is true"); + return true; +} +} // namespace OHOS +} // namespace DistributedKv diff --git a/services/distributeddataservice/adapter/dfx/src/dfx_code_constant.h b/services/distributeddataservice/adapter/dfx/src/dfx_code_constant.h new file mode 100755 index 000000000..8af17ce87 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/dfx_code_constant.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_DFX_CODE_CONSTANT_H +#define DISTRIBUTEDDATAMGR_DFX_CODE_CONSTANT_H + +class DfxCodeConstant { +public: + static inline const int SERVICE_FAULT = 950001100; + static inline const int RUNTIME_FAULT = 950001101; + static inline const int DATABASE_FAULT = 950001102; + static inline const int COMMUNICATION_FAULT = 950001103; + static inline const int DATABASE_STATISTIC = 950001104; + static inline const int VISIT_STATISTIC = 950001105; + static inline const int TRAFFIC_STATISTIC = 950001106; + static inline const int DATABASE_PERFORMANCE_STATISTIC = 950001107; + static inline const int API_PERFORMANCE_STATISTIC = 950001110; + static inline const int API_PERFORMANCE_INTERFACE = 950001111; +}; +#endif // DISTRIBUTEDDATAMGR_DFX_CODE_CONSTANT_H diff --git a/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.cpp b/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.cpp new file mode 100755 index 000000000..3e945cf7f --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.cpp @@ -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. + */ + +#include "communication_fault_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus CommunicationFaultImpl::Report(const FaultMsg &msg) +{ + HiViewAdapter::ReportFault(DfxCodeConstant::COMMUNICATION_FAULT, msg); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.h b/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.h new file mode 100755 index 000000000..77da42d1b --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/communication_fault_impl.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 DISTRIBUTEDDATAMGR_COMMUNICATION_FAULT_IMPL_H +#define DISTRIBUTEDDATAMGR_COMMUNICATION_FAULT_IMPL_H + +#include "fault_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class CommunicationFaultImpl : public FaultReporter { +public: + virtual ~CommunicationFaultImpl() {}; + ReportStatus Report(const struct FaultMsg &msg) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_COMMUNICATION_FAULT_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.cpp b/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.cpp new file mode 100755 index 000000000..aea97850e --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.cpp @@ -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. + */ + +#include "database_fault_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus DatabaseFaultImpl::Report(const FaultMsg &msg) +{ + HiViewAdapter::ReportFault(DfxCodeConstant::DATABASE_FAULT, msg); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.h b/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.h new file mode 100755 index 000000000..d320e12c5 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/database_fault_impl.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_DATABASE_FAULT_IMPL_H +#define DISTRIBUTEDDATAMGR_DATABASE_FAULT_IMPL_H + +#include "fault_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class DatabaseFaultImpl : public FaultReporter { +public: + virtual ~DatabaseFaultImpl() {} + ReportStatus Report(const FaultMsg &msg) override; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // DISTRIBUTEDDATAMGR_DATABASE_FAULT_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/fault/fault_reporter.cpp b/services/distributeddataservice/adapter/dfx/src/fault/fault_reporter.cpp new file mode 100755 index 000000000..dfe897c6a --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/fault_reporter.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "fault_reporter.h" + +namespace OHOS { +namespace DistributedKv { +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.cpp b/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.cpp new file mode 100755 index 000000000..d58aa2509 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.cpp @@ -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. + */ + +#include "runtime_fault_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus RuntimeFaultImpl::Report(const FaultMsg &msg) +{ + HiViewAdapter::ReportFault(DfxCodeConstant::RUNTIME_FAULT, msg); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.h b/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.h new file mode 100755 index 000000000..4902e853a --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/runtime_fault_impl.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 DISTRIBUTEDDATAMGR_RUNTIME_FAULT_IMPL_H +#define DISTRIBUTEDDATAMGR_RUNTIME_FAULT_IMPL_H +#include "hiview_adapter.h" +#include "fault_reporter.h" + +namespace OHOS { +namespace DistributedKv { +class RuntimeFaultImpl : public FaultReporter { +public: + virtual ~RuntimeFaultImpl() {} + ReportStatus Report(const FaultMsg &msg) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_RUNTIME_FAULT_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.cpp b/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.cpp new file mode 100755 index 000000000..10a4c61d9 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.cpp @@ -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. + */ + +#include "service_fault_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus ServiceFaultImpl::Report(const OHOS::DistributedKv::FaultMsg &msg) +{ + HiViewAdapter::ReportFault(DfxCodeConstant::SERVICE_FAULT, msg); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.h b/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.h new file mode 100755 index 000000000..20ce67bf3 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/fault/service_fault_impl.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 DISTRIBUTEDDATAMGR_SERVICE_FAULT_IMPL_H +#define DISTRIBUTEDDATAMGR_SERVICE_FAULT_IMPL_H + +#include "hiview_adapter.h" +#include "fault_reporter.h" + +namespace OHOS { +namespace DistributedKv { +class ServiceFaultImpl : public FaultReporter { +public: + virtual ~ServiceFaultImpl() {} + ReportStatus Report(const FaultMsg &msg) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_SERVICE_FAULT_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/hiview_adapter.cpp b/services/distributeddataservice/adapter/dfx/src/hiview_adapter.cpp new file mode 100755 index 000000000..8889d728a --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/hiview_adapter.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "HiViewAdapter" + +#include "hiview_adapter.h" +#include +#include +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +std::shared_ptr HiViewAdapter::pool_ = KvStoreThreadPool::GetPool(POOL_SIZE, true); + +std::mutex HiViewAdapter::visitMutex_; +std::map> HiViewAdapter::visitStat_; + +std::mutex HiViewAdapter::trafficMutex_; +std::map> HiViewAdapter::trafficStat_; + +std::mutex HiViewAdapter::dbMutex_; +std::map> HiViewAdapter::dbStat_; + +std::mutex HiViewAdapter::apiPerformanceMutex_; +std::map> HiViewAdapter::apiPerformanceStat_; + +bool HiViewAdapter::running_ = false; +std::mutex HiViewAdapter::runMutex_; + +void HiViewAdapter::ReportFault(int dfxCode, const FaultMsg &msg) +{ + KvStoreTask task([=]() { + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + std::to_string(dfxCode), + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + FAULT_TYPE, static_cast(msg.faultType), + MODULE_NAME, msg.moduleName, + INTERFACE_NAME, msg.interfaceName, + ERROR_TYPE, static_cast(msg.errorType)); + }); + if (pool_ != nullptr) { + pool_->AddTask(std::move(task)); + } +} + +void HiViewAdapter::ReportDatabaseStatistic(int dfxCode, const DbStat &stat) +{ + KvStoreTask task([=]() { + std::lock_guard lock(dbMutex_); + if (!dbStat_.count(stat.GetKey())) { + dbStat_.insert({stat.GetKey(), {stat, 0, dfxCode}}); + } + }); + if (pool_ != nullptr) { + pool_->AddTask(std::move(task)); + } + StartTimerThread(); +} + +void HiViewAdapter::ReportDbSize(const StatisticWrap &stat) +{ + uint64_t dbSize; + if (!stat.val.delegate->GetKvStoreDiskSize(stat.val.storeId, dbSize)) { + return; + } + + ValueHash vh; + std::string userId; + if (!vh.CalcValueHash(stat.val.userId, userId)) { + return; + } + + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + std::to_string(stat.code), + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + USER_ID, userId, APP_ID, stat.val.appId, STORE_ID, stat.val.storeId, DB_SIZE, dbSize); +} +void HiViewAdapter::InvokeDbSize() +{ + std::lock_guard lock(dbMutex_); + for (auto const &kv : dbStat_) { + if (kv.second.val.delegate == nullptr) { + continue; + } + // device coordinate for single version database + if (!kv.second.val.storeId.empty()) { + ReportDbSize(kv.second); + continue; + } + // below codes for multiple version database + std::vector storeInfos; + kv.second.val.delegate->GetKvStoreKeys(storeInfos); + if (storeInfos.empty()) { + continue; + } + for (auto const &storeInfo : storeInfos) { + ReportDbSize({{storeInfo.userId, storeInfo.appId, storeInfo.storeId, 0, + kv.second.val.delegate}, 0, kv.second.code}); + } + } + dbStat_.clear(); +} + +void HiViewAdapter::ReportTrafficStatistic(int dfxCode, const TrafficStat &stat) +{ + KvStoreTask task([=]() { + std::lock_guard lock(trafficMutex_); + auto it = trafficStat_.find(stat.GetKey()); + if (it != trafficStat_.end()) { + it->second.val.receivedSize += stat.receivedSize; + it->second.val.sendSize += stat.sendSize; + } else { + trafficStat_.insert({stat.GetKey(), {stat, 0, dfxCode}}); + } + }); + if (pool_ != nullptr) { + pool_->AddTask(std::move(task)); + } + StartTimerThread(); +} + +void HiViewAdapter::InvokeTraffic() +{ + std::lock_guard lock(trafficMutex_); + ValueHash vh; + for (auto const &kv : trafficStat_) { + std::string deviceId; + if (!vh.CalcValueHash(kv.second.val.deviceId, deviceId)) { + continue; + } + + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + std::to_string(kv.second.code), + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + APP_ID, kv.second.val.appId, + DEVICE_ID, deviceId, + SEND_SIZE, kv.second.val.sendSize, + RECEIVED_SIZE, kv.second.val.receivedSize); + } + trafficStat_.clear(); +} + +void HiViewAdapter::ReportVisitStatistic(int dfxCode, const VisitStat &stat) +{ + KvStoreTask task([=]() { + std::lock_guard lock(visitMutex_); + auto it = visitStat_.find(stat.GetKey()); + if (it == visitStat_.end()) { + visitStat_.insert({stat.GetKey(), {stat, 1, dfxCode}}); + } else { + it->second.times++; + } + }); + if (pool_ != nullptr) { + pool_->AddTask(std::move(task)); + } + StartTimerThread(); +} + +void HiViewAdapter::InvokeVisit() +{ + std::lock_guard lock(visitMutex_); + for (auto const &kv : visitStat_) { + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + std::to_string(kv.second.code), + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + APP_ID, kv.second.val.appId, + INTERFACE_NAME, kv.second.val.interfaceName, + TIMES, kv.second.times); + } + visitStat_.clear(); +} + +void HiViewAdapter::ReportApiPerformanceStatistic(int dfxCode, const ApiPerformanceStat &stat) +{ + KvStoreTask task([=]() { + std::lock_guard lock(apiPerformanceMutex_); + auto it = apiPerformanceStat_.find(stat.GetKey()); + if (it == apiPerformanceStat_.end()) { + apiPerformanceStat_.insert({stat.GetKey(), {stat, 1, dfxCode}}); // the init value of times is 1 + } else { + it->second.times++; + it->second.val.costTime = stat.costTime; + if (it->second.times > 0) { + it->second.val.averageTime = (it->second.val.averageTime * (it->second.times - 1) + stat.costTime) + / it->second.times; + } + if (stat.costTime > it->second.val.worstTime) { + it->second.val.worstTime = stat.costTime; + } + } + }); + if (pool_ != nullptr) { + pool_->AddTask(std::move(task)); + } + StartTimerThread(); +} + +void HiViewAdapter::InvokeApiPerformance() +{ + std::string message; + message.append("["); + std::lock_guard lock(apiPerformanceMutex_); + for (auto const &kv : apiPerformanceStat_) { + message.append("{\"CODE\":\"").append(std::to_string(kv.second.code)).append("\",") + .append("\"").append(INTERFACE_NAME).append("\":\"").append(kv.second.val.interfaceName).append("\",") + .append("\"").append(TIMES).append("\":").append(std::to_string(kv.second.times)).append(",") + .append("\"").append(AVERAGE_TIMES).append("\":").append(std::to_string(kv.second.val.averageTime)).append(",") + .append("\"").append(WORST_TIMES).append("\":").append(std::to_string(kv.second.val.worstTime)).append("}"); + } + message.append("]"); + OHOS::HiviewDFX::HiSysEvent::Write(OHOS::HiviewDFX::HiSysEvent::Domain::DISTRIBUTED_DATAMGR, + std::to_string(DfxCodeConstant::API_PERFORMANCE_STATISTIC), + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, + INTERFACES, message); + apiPerformanceStat_.clear(); + ZLOGI("DdsTrace interface: clean"); +} + +void HiViewAdapter::StartTimerThread() +{ + if (running_) { + return; + } + std::lock_guard lock(runMutex_); + if (running_) { + return; + } + running_ = true; + auto fun = [=]() { + while (true) { + time_t current = time(nullptr); + tm *localTime = localtime(¤t); + if (localTime == nullptr) { + continue; + } + int currentHour = localTime->tm_hour; + if (currentHour < EXEC_TIME) { + sleep((EXEC_TIME - currentHour) * SIXTY_SEC * SIXTY_SEC); + InvokeDbSize(); + InvokeTraffic(); + InvokeVisit(); + InvokeApiPerformance(); + } else { + sleep(WAIT_TIME); + } + } + }; + std::thread th = std::thread(fun); + th.detach(); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/dfx/src/hiview_adapter.h b/services/distributeddataservice/adapter/dfx/src/hiview_adapter.h new file mode 100755 index 000000000..35205f713 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/hiview_adapter.h @@ -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. + */ + +#ifndef DISTRIBUTEDDATAMGR_HI_VIEW_ADAPTER_H +#define DISTRIBUTEDDATAMGR_HI_VIEW_ADAPTER_H + +#include +#include +#include "dfx_types.h" +#include "dfx_code_constant.h" +#include "hisysevent.h" +#include "kv_store_thread_pool.h" +#include "kv_store_task.h" +#include "value_hash.h" + +namespace OHOS { +namespace DistributedKv { +template +struct StatisticWrap { + T val; + int times; + int code; +}; + +class HiViewAdapter { +public: + ~HiViewAdapter(); + static void ReportFault(int dfxCode, const FaultMsg &msg); + static void ReportVisitStatistic(int dfxCode, const VisitStat &stat); + static void ReportTrafficStatistic(int dfxCode, const TrafficStat &stat); + static void ReportDatabaseStatistic(int dfxCode, const DbStat &stat); + static void ReportApiPerformanceStatistic(int dfxCode, const ApiPerformanceStat &stat); + static void StartTimerThread(); + +private: + static constexpr int POOL_SIZE = 3; + static std::shared_ptr pool_; + + static std::mutex visitMutex_; + static std::map> visitStat_; + static void InvokeVisit(); + + static std::mutex trafficMutex_; + static std::map> trafficStat_; + static void InvokeTraffic(); + + static std::mutex dbMutex_; + static std::map> dbStat_; + static void InvokeDbSize(); + static void ReportDbSize(const StatisticWrap &stat); + + static std::mutex apiPerformanceMutex_; + static std::map> apiPerformanceStat_; + static void InvokeApiPerformance(); + +private: + // fault key + static const inline std::string FAULT_TYPE = "FAULT_TYPE"; + static const inline std::string MODULE_NAME = "MODULE_NAME"; + static const inline std::string INTERFACE_NAME = "INTERFACE_NAME"; + static const inline std::string ERROR_TYPE = "ERROR_TYPE"; + + // Database statistic + static const inline std::string USER_ID = "ANONYMOUS_UID"; + static const inline std::string APP_ID = "APP_ID"; + static const inline std::string STORE_ID = "STORE_ID"; + static const inline std::string DB_SIZE = "DB_SIZE"; + + // interface visit statistic + static const inline std::string TIMES = "TIMES"; + static const inline std::string DEVICE_ID = "ANONYMOUS_DID"; + static const inline std::string SEND_SIZE = "SEND_SIZE"; + static const inline std::string RECEIVED_SIZE = "RECEIVED_SIZE"; + static const inline std::string COMPLETE_TIME = "COMPLETE_TIME"; + static const inline std::string SIZE = "SIZE"; + static const inline std::string SRC_DEVICE_ID = "ANONYMOUS_SRC_DID"; + static const inline std::string DST_DEVICE_ID = "ANONYMOUS_DST_DID"; + static const inline std::string AVERAGE_TIMES = "AVERAGE_TIME"; + static const inline std::string WORST_TIMES = "WORST_TIME"; + static const inline std::string INTERFACES = "INTERFACES"; +private: + static std::mutex runMutex_; + static bool running_; + static const inline int EXEC_TIME = 23; + static const inline int SIXTY_SEC = 60; + + static const inline int WAIT_TIME = 2 * 60 * 60; // 2 hours + static const inline int PERIOD_TIME_US = 1 * 1000 * 1000; // 1 s +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_HI_VIEW_ADAPTER_H diff --git a/services/distributeddataservice/adapter/dfx/src/reporter.cpp b/services/distributeddataservice/adapter/dfx/src/reporter.cpp new file mode 100755 index 000000000..1b26eff3c --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/reporter.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 "reporter.h" +#include "communication_fault_impl.h" +#include "database_fault_impl.h" +#include "runtime_fault_impl.h" +#include "service_fault_impl.h" + +#include "traffic_statistic_impl.h" +#include "visit_statistic_impl.h" +#include "database_statistic_impl.h" +#include "api_performance_statistic_impl.h" + +namespace OHOS { +namespace DistributedKv { +Reporter* Reporter::GetInstance() +{ + static Reporter reporter; + return &reporter; +} + +FaultReporter* Reporter::CommunicationFault() +{ + static CommunicationFaultImpl communicationFault; + return &communicationFault; +} + +FaultReporter* Reporter::DatabaseFault() +{ + static DatabaseFaultImpl databaseFault; + return &databaseFault; +} + +FaultReporter* Reporter::RuntimeFault() +{ + static RuntimeFaultImpl runtimeFault; + return &runtimeFault; +} + +FaultReporter* Reporter::ServiceFault() +{ + static ServiceFaultImpl serviceFault; + return &serviceFault; +} + +StatisticReporter* Reporter::TrafficStatistic() +{ + static TrafficStatisticImpl trafficStatistic; + return &trafficStatistic; +} + +StatisticReporter* Reporter::VisitStatistic() +{ + static VisitStatisticImpl visitStatistic; + return &visitStatistic; +} + +StatisticReporter* Reporter::DatabaseStatistic() +{ + static DatabaseStatisticImpl databaseStatistic; + return &databaseStatistic; +} + +StatisticReporter* Reporter::ApiPerformanceStatistic() +{ + static ApiPerformanceStatisticImpl apiPerformanceStat; + return &apiPerformanceStat; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.cpp b/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.cpp new file mode 100755 index 000000000..13e7c1c71 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "api_performance_statistic_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus ApiPerformanceStatisticImpl::Report(const ApiPerformanceStat &stat) +{ + HiViewAdapter::ReportApiPerformanceStatistic(DfxCodeConstant::API_PERFORMANCE_INTERFACE, stat); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS + diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.h b/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.h new file mode 100755 index 000000000..69054651b --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/api_performance_statistic_impl.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_API_PERFORMANCE_STATISTIC_IMPL_H +#define DISTRIBUTEDDATAMGR_API_PERFORMANCE_STATISTIC_IMPL_H + +#include "statistic_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class ApiPerformanceStatisticImpl : public StatisticReporter { +public: + virtual ~ApiPerformanceStatisticImpl() {} + ReportStatus Report(const ApiPerformanceStat &stat) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_API_PERFORMANCE_STATISTIC_IMPL_H + diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.cpp b/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.cpp new file mode 100755 index 000000000..ef74975ad --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.cpp @@ -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. + */ + +#include "database_statistic_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus DatabaseStatisticImpl::Report(const DbStat &stat) +{ + HiViewAdapter::ReportDatabaseStatistic(DfxCodeConstant::DATABASE_STATISTIC, stat); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.h b/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.h new file mode 100755 index 000000000..c61e54042 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/database_statistic_impl.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 DISTRIBUTEDDATAMGR_DATABASE_STATISTIC_IMPL_H +#define DISTRIBUTEDDATAMGR_DATABASE_STATISTIC_IMPL_H + +#include "statistic_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class DatabaseStatisticImpl : public StatisticReporter { +public: + virtual ~DatabaseStatisticImpl() {} + ReportStatus Report(const DbStat &stat) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_DATABASE_STATISTIC_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/statistic_reporter.cpp b/services/distributeddataservice/adapter/dfx/src/statistic/statistic_reporter.cpp new file mode 100755 index 000000000..70caebd36 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/statistic_reporter.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "statistic_reporter.h" + +namespace OHOS { +namespace DistributedKv { +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.cpp b/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.cpp new file mode 100755 index 000000000..7907a5662 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.cpp @@ -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. + */ + +#include "traffic_statistic_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus TrafficStatisticImpl::Report(const TrafficStat &stat) +{ + HiViewAdapter::ReportTrafficStatistic(DfxCodeConstant::TRAFFIC_STATISTIC, stat); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.h b/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.h new file mode 100755 index 000000000..30125a632 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/traffic_statistic_impl.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 DISTRIBUTEDDATAMGR_TRAFFIC_STATISTIC_IMPL_H +#define DISTRIBUTEDDATAMGR_TRAFFIC_STATISTIC_IMPL_H + +#include "statistic_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class TrafficStatisticImpl : public StatisticReporter { +public: + virtual ~TrafficStatisticImpl() {} + ReportStatus Report(const TrafficStat &stat) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_TRAFFIC_STATISTIC_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.cpp b/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.cpp new file mode 100755 index 000000000..584464522 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.cpp @@ -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. + */ + +#include "visit_statistic_impl.h" + +namespace OHOS { +namespace DistributedKv { +ReportStatus VisitStatisticImpl::Report(const VisitStat &stat) +{ + HiViewAdapter::ReportVisitStatistic(DfxCodeConstant::VISIT_STATISTIC, stat); + return ReportStatus::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.h b/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.h new file mode 100755 index 000000000..e925724d0 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/statistic/visit_statistic_impl.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 DISTRIBUTEDDATAMGR_VISIT_STATISTIC_IMPL_H +#define DISTRIBUTEDDATAMGR_VISIT_STATISTIC_IMPL_H + +#include "statistic_reporter.h" +#include "hiview_adapter.h" + +namespace OHOS { +namespace DistributedKv { +class VisitStatisticImpl : public StatisticReporter { +public: + virtual ~VisitStatisticImpl() {} + ReportStatus Report(const VisitStat &stat) override; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_VISIT_STATISTIC_IMPL_H diff --git a/services/distributeddataservice/adapter/dfx/src/value_hash.h b/services/distributeddataservice/adapter/dfx/src/value_hash.h new file mode 100644 index 000000000..5ee7da11b --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/src/value_hash.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 DISTRIBUTEDDATAMGR_VALUE_HASH_H +#define DISTRIBUTEDDATAMGR_VALUE_HASH_H + +#include +#include + +namespace OHOS { +namespace DistributedKv { +class ValueHash { +public: + ValueHash() {}; + ~ValueHash() {}; + + bool CalcValueHash(const std::string &input, std::string &res) + { + SHA256_CTX context; + std::vector value(input.begin(), input.end()); + if (!SHA256_Init(&context)) { + return false; + } + if (!SHA256_Update(&context, value.data(), value.size())) { + return false; + } + + std::vector result; + result.resize(SHA256_DIGEST_LENGTH); + if (!SHA256_Final(result.data(), &context)) { + return false; + } + if (result.size() < LEN) { + res = std::string(result.begin(), result.end()); + return true; + } + res = std::string(result.end() - LEN, result.end()); + return true; + } +private: + static constexpr int LEN = 8; // 8 is the substring length +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_VALUE_HASH_H diff --git a/services/distributeddataservice/adapter/dfx/test/BUILD.gn b/services/distributeddataservice/adapter/dfx/test/BUILD.gn new file mode 100755 index 000000000..da12bc4ab --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/BUILD.gn @@ -0,0 +1,121 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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/distributeddatafwk" + +############################################################################### +config("module_dfx_mst_config") { + visibility = [ ":*" ] + + include_dirs = [ + "../src", + "../src/fault", + "../src/statistic", + "../../include/log", + "../../include/autils", + "../../include/dfx", + "//utils/native/base/include", + "//utils/system/safwk/native/include", + "//third_party/openssl/include/", + ] +} + +ohos_unittest("DistributeddataDfxMSTTest") { + module_out_path = module_output_path + + sources = [ "./unittest/distributeddata_dfx_mst_test.cpp" ] + + configs = [ ":module_dfx_mst_config" ] + + external_deps = [ + "bytrace_standard:bytrace_core", + "hisysevent_native:libhisysevent", + "hiviewdfx_hilog_native:libhilog", + ] + + deps = [ + "../../autils:distributeddata_autils_static", + "//third_party/openssl:libcrypto_static", + "../../dfx:distributeddata_dfx_static", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +############################################################################### +config("module_dfx_config") { + visibility = [ ":*" ] + + include_dirs = [ + "./unittest/fake/hiview/include", + "./unittest/fake/hiview", + "../src", + "../src/fault", + "../src/statistic", + "../../include/log", + "../../include/autils", + "../../include/dfx", + "//utils/native/base/include", + "//utils/system/safwk/native/include", + "//third_party/openssl/include/", + ] +} + +ohos_unittest("DistributeddataDfxUTTest") { + module_out_path = module_output_path + + sources = [ + "../src/fault/communication_fault_impl.cpp", + "../src/fault/database_fault_impl.cpp", + "../src/fault/fault_reporter.cpp", + "../src/fault/runtime_fault_impl.cpp", + "../src/fault/service_fault_impl.cpp", + "../src/hiview_adapter.cpp", + "../src/reporter.cpp", + "../src/statistic/api_performance_statistic_impl.cpp", + "../src/statistic/database_statistic_impl.cpp", + "../src/statistic/statistic_reporter.cpp", + "../src/statistic/traffic_statistic_impl.cpp", + "../src/statistic/visit_statistic_impl.cpp", + "./unittest/distributeddata_dfx_ut_test.cpp", + "./unittest/fake/hiview/fake_hiview.cpp", + ] + + configs = [ ":module_dfx_config" ] + + external_deps = [ + "bytrace_standard:bytrace_core", + "hisysevent_native:libhisysevent", + "hiviewdfx_hilog_native:libhilog", + ] + + deps = [ + "../../autils:distributeddata_autils_static", + "//third_party/openssl:libcrypto_static", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +############################################################################### +group("unittest") { + testonly = true + deps = [] + + deps += [ + ":DistributeddataDfxMSTTest", + ":DistributeddataDfxUTTest", + ] +} +############################################################################### diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_mst_test.cpp b/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_mst_test.cpp new file mode 100755 index 000000000..4c08dae0f --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_mst_test.cpp @@ -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. + */ + +#include +#include +#include "reporter.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class DistributedataDfxMSTTest : public testing::Test { +public: + static void SetUpTestCase(void); + + static void TearDownTestCase(void); + + void SetUp(); + + void TearDown(); +}; + +void DistributedataDfxMSTTest::SetUpTestCase() {} + +void DistributedataDfxMSTTest::TearDownTestCase() {} + +void DistributedataDfxMSTTest::SetUp() {} + +void DistributedataDfxMSTTest::TearDown() {} + +/** + * @tc.name: Dfx001 + * @tc.desc: send data to 1 device, then check reporter message. + * @tc.type: send data + * @tc.require: AR000CQE1L + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxMSTTest, Dfx001, TestSize.Level0) +{ + /** + * @tc.steps: step1. getcommunicationFault instance + * @tc.expected: step1. Expect instance is not null. + */ + auto comFault = Reporter::GetInstance()->CommunicationFault(); + EXPECT_NE(nullptr, comFault); + struct FaultMsg msg{.faultType= FaultType::SERVICE_FAULT, .moduleName = "comm", .interfaceName = "sendData", + .errorType = Fault::CF_CREATE_SESSION}; + auto repStatus = comFault->Report(msg); + EXPECT_TRUE(repStatus == ReportStatus::SUCCESS); + + std::string myuid = "203230afadj020003"; + std::vector value(myuid.begin(), myuid.end()); + std::vector result; + + SHA256_CTX *context = new (std::nothrow) SHA256_CTX; + if (context == nullptr || !SHA256_Init(context)) { + delete context; + return; + } + + if (!SHA256_Update(context, value.data(), value.size())) { + delete context; + return; + } + result.resize(SHA256_DIGEST_LENGTH); + SHA256_Final(result.data(), context); + delete context; +} diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_ut_test.cpp b/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_ut_test.cpp new file mode 100755 index 000000000..fe6d99095 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/distributeddata_dfx_ut_test.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "reporter.h" +#include "fake_hiview.h" +#include "value_hash.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class DistributedataDfxUTTest : public testing::Test { +public: + static void SetUpTestCase(void); + + static void TearDownTestCase(void); + + void SetUp(); + + void TearDown(); +}; + +void DistributedataDfxUTTest::SetUpTestCase() +{ + FakeHivew::Clear(); +} + +void DistributedataDfxUTTest::TearDownTestCase() +{ + FakeHivew::Clear(); +} + +void DistributedataDfxUTTest::SetUp() {} + +void DistributedataDfxUTTest::TearDown() {} + +/** + * @tc.name: Dfx001 + * @tc.desc: send data to 1 device, then check reporter message. + * @tc.type: send data + * @tc.require: AR000CQE1L SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx001, TestSize.Level0) +{ + /** + * @tc.steps: step1. getcommunicationFault instance + * @tc.expected: step1. Expect instance is not null. + */ + auto comFault = Reporter::GetInstance()->CommunicationFault(); + EXPECT_NE(nullptr, comFault); + struct FaultMsg msg{.faultType= FaultType::SERVICE_FAULT, .moduleName = "comm", .interfaceName = "sendData", + .errorType = Fault::CF_CREATE_SESSION}; + auto repStatus = comFault->Report(msg); + EXPECT_TRUE(repStatus == ReportStatus::SUCCESS); + /** + * @tc.steps:step2. check dfx reporter. + * @tc.expected: step2. Expect report message success. + */ + std::string val = FakeHivew::GetString("MODULE_NAME"); + if (!val.empty()) { + EXPECT_STREQ(val.c_str(), string("comm").c_str()); + } + auto typeVal = FakeHivew::GetInt("ERROR_TYPE"); + if (typeVal > 0) { + EXPECT_EQ(typeVal, static_cast(Fault::CF_CREATE_SESSION)); + } + FakeHivew::Clear(); +} + +/** + * @tc.name: Dfx002 + * @tc.desc: check database dfx report + * @tc.type: get kvStore + * @tc.require: AR000CQE1L AR000CQE1K SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx002, TestSize.Level0) +{ + FakeHivew::Clear(); + /** + * @tc.steps: step1. get database fault report instance + * @tc.expected: step1. Expect get instance success. + */ + auto dbFault = Reporter::GetInstance()->DatabaseFault(); + EXPECT_NE(nullptr, dbFault); + struct FaultMsg msg{.faultType= FaultType::SERVICE_FAULT, .moduleName = "database", + .interfaceName = "getKvStore", + .errorType = Fault::DF_DB_DAMAGE}; + auto repStatus = dbFault->Report(msg); + /** + * @tc.steps: step2. check fault reporter. + * @tc.expected: step2. Expect report has bad report message. + */ + EXPECT_TRUE(repStatus == ReportStatus::SUCCESS); + auto val = FakeHivew::GetString("MODULE_NAME"); + if (!val.empty()) { + EXPECT_STREQ(string("database").c_str(), val.c_str()); + } + auto typeVal = FakeHivew::GetInt("ERROR_TYPE"); + if (typeVal > 0) { + EXPECT_EQ(static_cast(Fault::DF_DB_DAMAGE), typeVal); + } + FakeHivew::Clear(); +} + +/** + * @tc.name: Dfx003 + * @tc.desc: Database file size statistic. + * @tc.type: check database file size. + * @tc.require: AR000CQE1O SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx003, TestSize.Level0) +{ + /** + * @tc.steps: step1. get database reporter instance. + * @tc.expected: step1. Expect get success. + */ + auto dbs = Reporter::GetInstance()->DatabaseStatistic(); + EXPECT_NE(nullptr, dbs); + DbStat ds = {"uid", "appid", "storeId001", 100}; + auto dbsRet = dbs->Report(ds); + /** + * @tc.steps:step2. check reporter. + * @tc.expected: step2. Expect statistic database size is 100. + */ + EXPECT_TRUE(dbsRet == ReportStatus::SUCCESS); + FakeHivew::Clear(); +} + +/** + * @tc.name: Dfx004 + * @tc.desc: Set invalid information, call getKvStore, expect return INVALID_ARGS. + * @tc.type: CreateKvStore test + * @tc.require: AR000CQE1L SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx004, TestSize.Level0) +{ + /** + * @tc.steps: step1. Get runtime fault instance. + * @tc.expected: step1. Expect get runtime fault instance success. + */ + auto rtFault = Reporter::GetInstance()->RuntimeFault(); + auto rtFault2 = Reporter::GetInstance()->RuntimeFault(); + EXPECT_NE(nullptr, rtFault); + EXPECT_EQ(rtFault, rtFault2); + + struct FaultMsg rfMsg{FaultType::SERVICE_FAULT, "database", "closeKvStore", + Fault::RF_CLOSE_DB}; + auto rfReportRet = rtFault->Report(rfMsg); + /** + * @tc.steps:step2. check report message. + * @tc.expected: step2. Expected reported message. + */ + EXPECT_TRUE(rfReportRet == ReportStatus::SUCCESS); + auto val = FakeHivew::GetString("INTERFACE_NAME"); + if (!val.empty()) { + EXPECT_STREQ(string("closeKvStore").c_str(), val.c_str()); + } + auto typeVal = FakeHivew::GetInt("ERROR_TYPE"); + if (typeVal > 0) { + EXPECT_EQ(static_cast(Fault::RF_CLOSE_DB), typeVal); + } + FakeHivew::Clear(); +} + +/** + * @tc.name: Dfx005 + * @tc.desc: send data to 1 device, then check send size. + * @tc.type: send data + * @tc.require: AR000CQE1P SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx005, TestSize.Level0) +{ + /** + * @tc.steps:step1. send data to 1 device + * @tc.expected: step1. Expect put success. + */ + auto ts = Reporter::GetInstance()->TrafficStatistic(); + EXPECT_NE(nullptr, ts); + struct TrafficStat tss = {"appId001", "deviceId001", 100, 200}; + auto tsRet = ts->Report(tss); + /** + * @tc.steps:step2. check dfx reporter. + * @tc.expected: step2. Expect report has same size. + */ + EXPECT_TRUE(tsRet == ReportStatus::SUCCESS); + + FakeHivew::Clear(); +} + +/** + * @tc.name: Dfx006 + * @tc.desc: call interface statistic. + * @tc.type: statistic + * @tc.require: AR000CQE1N SR000CQE1J + * @tc.author: hongbo + */ +HWTEST_F(DistributedataDfxUTTest, Dfx006, TestSize.Level0) +{ + /** + * @tc.steps:step1. create call interface statistic instance + * @tc.expected: step1. Expect get instance success. + */ + auto vs = Reporter::GetInstance()->VisitStatistic(); + EXPECT_NE(nullptr, vs); + struct VisitStat vss = {"appid001", "Put"}; + auto vsRet = vs->Report(vss); + /** + * @tc.steps:step2. check dfx reporter. + * @tc.expected: step2. Expect report has same information. + */ + EXPECT_TRUE(vsRet == ReportStatus::SUCCESS); + + FakeHivew::Clear(); + std::string myuid = "203230afadj020003"; + std::string result; + ValueHash vh; + vh.CalcValueHash(myuid, result); +} + +/** + * @tc.name: Dfx007 + * @tc.desc: call api performance statistic. + * @tc.type: statistic + * @tc.require: AR000DPVGP SR000DPVGH + * @tc.author: liwei + */ +HWTEST_F(DistributedataDfxUTTest, Dfx007, TestSize.Level0) +{ + /** + * @tc.steps:step1. create call api perforamnce statistic instance + * @tc.expected: step1. Expect get instance success. + */ + auto ap = Reporter::GetInstance()->ApiPerformanceStatistic(); + EXPECT_NE(nullptr, ap); + struct ApiPerformanceStat aps = { "interface", 10000, 5000, 20000 }; + auto apRet = ap->Report(aps); + /** + * @tc.steps:step2. check dfx reporter return value. + * @tc.expected: step2. Expect report has same information. + */ + EXPECT_TRUE(apRet == ReportStatus::SUCCESS); + + FakeHivew::Clear(); +} + diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/fake_hiview.cpp b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/fake_hiview.cpp new file mode 100755 index 000000000..1f58aa670 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/fake_hiview.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/fake_hiview.h" +std::map FakeHivew::fakeCache_; diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hievent.cpp b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hievent.cpp new file mode 100755 index 000000000..8371e7fbe --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hievent.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/hievent.h" diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hiview.cpp b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hiview.cpp new file mode 100755 index 000000000..084c07321 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/hiview.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/hiview.h" diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/fake_hiview.h b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/fake_hiview.h new file mode 100644 index 000000000..5cd962ba4 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/fake_hiview.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 DISTRIBUTEDDATAMGR_HIVIEW_MGR_H +#define DISTRIBUTEDDATAMGR_HIVIEW_MGR_H + +#include +#include +#include +#include + +class FakeHivew { +public: + static std::string GetString(const std::string &key) + { + usleep(INTERNAL); + if (fakeCache_.count(key)) { + return std::any_cast(fakeCache_[key]); + } + return ""; + } + + static int GetInt(const std::string &key) + { + usleep(INTERNAL); + if (fakeCache_.count(key)) { + return std::any_cast(fakeCache_[key]); + } + return 0; + } + + static void Put(const std::string &key, const std::any &an) + { + fakeCache_.insert({key, an}); + } + + static void Clear() + { + fakeCache_.clear(); + } + +private: + static std::map fakeCache_; + static const inline int INTERNAL = 1000; +}; + +#endif // DISTRIBUTEDDATAMGR_HIVIEW_MGR_H diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hievent.h b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hievent.h new file mode 100644 index 000000000..9114a2b87 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hievent.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HIVIEW_HIEVENT_H +#define HIVIEW_HIEVENT_H + +#include +#include +#include +#include +#include + +#include "fake_hiview.h" +/** + * HiEvent is common event that can be uploaded to cloud by hiview. + */ +class HiEvent { +public: + explicit HiEvent(int id) + { + id_ = id; + } + + virtual ~HiEvent() {}; + + HiEvent(const HiEvent& other) + { + } + + HiEvent& operator=(const HiEvent& other) + { + return *this; + } + + HiEvent(HiEvent&& other) {}; + HiEvent& operator=(HiEvent&& other) + { + return *this; + } + + /** + * Max length of payload's key. + */ + static const size_t MAX_KEY_LEN = 32; + + /** + * Max length of payload's value which type is string. + */ + static const size_t MAX_VALUE_LEN = 128; + + /** + * Max number of payload's pairs. + */ + static const size_t MAX_PAIR_NUM = 256; + + /** + * Max size of payload's array. + */ + static const size_t MAX_ARR_SIZE = 100; + + /** + * Max length of payload's path of file. + */ + static const size_t MAX_PATH_LEN = 256; + + /** + * Max number of payload's file. + */ + static const size_t MAX_FILE_NUM = 10; + + /** + * Put bool or array of bool as payload of this Hievent. + */ + HiEvent& PutBool(const std::string& key, bool value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutBoolArray(const std::string& key, const std::vector& value); + + /** + * Put byte or array of byte as payload of this Hievent. + */ + HiEvent& PutByte(const std::string& key, unsigned char value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutByteArray(const std::string& key, const std::vector& value); + + /** + * Put short or array of short as payload of this Hievent. + */ + HiEvent& PutShort(const std::string& key, short value) + { + FakeHivew::Put(key, value); + return *this; + } + + HiEvent& PutShortArray(const std::string& key, const std::vector& value); + + /** + * Put int or array of int as payload of this Hievent. + */ + HiEvent& PutInt(const std::string& key, int value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutIntArray(const std::string& key, const std::vector& value); + + /** + * Put long or array of long as payload of this Hievent. + */ + HiEvent& PutLong(const std::string& key, long value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutLongArray(const std::string& key, const std::vector& value); + + /** + * Put float or array of float as payload of this Hievent. + */ + HiEvent& PutFloat(const std::string& key, float value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutFloatArray(const std::string& key, const std::vector& value); + + /** + * Put string or array of string as payload of this Hievent. + */ + HiEvent& PutString(const std::string& key, const std::string& value) + { + FakeHivew::Put(key, value); + return *this; + } + HiEvent& PutStringArray(const std::string& key, const std::vector& value); + + /** + * Put sub HiEvent or array of HiEvent as payload of this Hievent. + */ + HiEvent& PutHiEvent(const std::string& key, HiEvent& value); + HiEvent& PutHiEventArray(const std::string& key, const std::vector& value) + { + return *this; + } + + /** + * Add file path to HiEvent. + */ + HiEvent& AddFilePath(const std::string& path); + + /** + * Reset the HiEvent. + */ + HiEvent& Reset(); + + int GetId() const; + long GetTime() const; + + /** + * Flatten the HiEvent as a string. + */ + const std::string& Flatten(); + + /** + * Set happen time of the HiEvent, if not, default one will be set. + */ + HiEvent& SetTime(long second); + +private: + int id_{}; +}; + +#endif diff --git a/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hiview.h b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hiview.h new file mode 100644 index 000000000..745f570f5 --- /dev/null +++ b/services/distributeddataservice/adapter/dfx/test/unittest/fake/hiview/include/hiview.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HIVIEW_HIVIEW_H +#define HIVIEW_HIVIEW_H + +#include +#include + +#include "hievent.h" + +class HiView { +public: + + /** + * A abstract class for which classes can be report by HiView. + */ + class Reportable { + public: + virtual ~Reportable() {} + virtual HiEvent& ToHiEvent() = 0; + }; + + /** + * Report a HiEvent. + * @param event reference of l-value. + */ + static void Report(HiEvent& event) + { + } + + /** + * Report a HiEvent. + * @param event reference of r-value. + */ + static void Report(HiEvent&& event); + + /** + * Report a Reportable. + * @param reportable self-defined class which implement Reportable. + */ + static void Report(HiView::Reportable& reportable); + + /** + * Create a HiEvent by single bool value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, bool value); + + /** + * Create a HiEvent by single byte value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, unsigned char value); + + /** + * Create a HiEvent by single byte value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, int value); + + /** + * Create a HiEvent by single byte value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, long value); + + /** + * Create a HiEvent by single byte value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, float value); + + /** + * Create a HiEvent by single byte value. + * @param eventID ID of event. + * @param key single payload's key. + * @param value single payload's value. + * + * @return HiEvent + */ + static HiEvent ByPair(int eventID, const std::string& key, const std::string& value); + + /** + * Create a HiEvent by json formatten string. + * @param eventID ID of event. + * @param key single payload's key. + * @param value json formatten string. + * + * @return HiEvent + */ + static HiEvent ByJson(int eventID, const std::string& json); +private: + static void ReportImpl(HiEvent&& event); +}; + +#endif diff --git a/services/distributeddataservice/adapter/include/account/account_delegate.h b/services/distributeddataservice/adapter/include/account/account_delegate.h new file mode 100755 index 000000000..d25a21db1 --- /dev/null +++ b/services/distributeddataservice/adapter/include/account/account_delegate.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 DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_H +#define DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_H + +#include +#include +#include "types.h" +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +enum class AccountStatus { + HARMONY_ACCOUNT_LOGIN = 0, // the openHarmony account is logged in + HARMONY_ACCOUNT_LOGOUT, // the openHarmony account is logged out + HARMONY_ACCOUNT_DELETE, // the openHarmony account is deleted + DEVICE_ACCOUNT_DELETE, // the device account is deleted +}; + +struct AccountEventInfo { + std::string harmonyAccountId; + std::string deviceAccountId; + AccountStatus status; +}; + +class AccountDelegate { +public: + class Observer { + public: + KVSTORE_API virtual ~Observer() = default; + KVSTORE_API virtual void OnAccountChanged(const AccountEventInfo &eventInfo) = 0; + + // must specify unique name for observer + KVSTORE_API virtual std::string Name() = 0; + }; + KVSTORE_API virtual ~AccountDelegate() = default; + KVSTORE_API virtual Status Subscribe(std::shared_ptr observer) = 0; + KVSTORE_API virtual Status Unsubscribe(std::shared_ptr observer) = 0; + KVSTORE_API virtual std::string GetCurrentHarmonyAccountId(const std::string &bundleName = "") const = 0; + KVSTORE_API virtual std::string GetDeviceAccountIdByUID(int32_t uid) const = 0; + KVSTORE_API virtual void SubscribeAccountEvent() = 0; + KVSTORE_API static AccountDelegate *GetInstance(); + const static std::string MAIN_DEVICE_ACCOUNT_ID; +private: + using BaseInstance = AccountDelegate *(*)(); + static BaseInstance getInstance_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_ACCOUNT_DELEGATE_H diff --git a/services/distributeddataservice/adapter/include/account/visibility.h b/services/distributeddataservice/adapter/include/account/visibility.h new file mode 100644 index 000000000..2ffb65244 --- /dev/null +++ b/services/distributeddataservice/adapter/include/account/visibility.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/services/distributeddataservice/adapter/include/autils/concurrent_map.h b/services/distributeddataservice/adapter/include/autils/concurrent_map.h new file mode 100644 index 000000000..5bd1b8637 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/concurrent_map.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 DISTRIBUTEDDATAMGR_CURRENT_MAP_H +#define DISTRIBUTEDDATAMGR_CURRENT_MAP_H + +#include +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +template +class ConcurrentMap { +public: + KVSTORE_API explicit ConcurrentMap() {}; + + KVSTORE_API ~ConcurrentMap() {}; + + KVSTORE_API bool Put(const K &key, const V &value) + { + std::unique_lock lock(mapMutex_); + auto ret = map_.insert(std::pair(key, value)); + return ret.second; + } + + KVSTORE_API bool PutValue(const K key, const V value) + { + std::unique_lock lock(mapMutex_); + auto ret = map_.insert(std::pair(key, value)); + return ret.second; + } + + KVSTORE_API bool Empty() + { + std::lock_guard lock(mapMutex_); + return map_.empty(); + } + + KVSTORE_API int Size() + { + std::lock_guard lock(mapMutex_); + return map_.size(); + } + + KVSTORE_API bool Delete(const K &key) + { + std::unique_lock lock(mapMutex_); + return map_.erase(key); + } + + KVSTORE_API bool Get(const K &key, V &v) + { + std::lock_guard lock(mapMutex_); + auto ret = map_.find(key); + if (ret != map_.end()) { + v = ret->second; + return true; + } + return false; + } + + KVSTORE_API bool ContainsKey(const K &key) + { + std::lock_guard lock(mapMutex_); + return map_.count(key); + } + + KVSTORE_API void Clear() + { + std::unique_lock lock(mapMutex_); + map_.clear(); + } + + KVSTORE_API void ForEach(std::function fun) + { + std::lock_guard lock(mapMutex_); + for (auto const &kvPair : map_) { + fun(kvPair.first, kvPair.second); + } + } +private: + std::mutex mapMutex_; + std::map map_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_CURRENT_MAP_H diff --git a/services/distributeddataservice/adapter/include/autils/constant.h b/services/distributeddataservice/adapter/include/autils/constant.h new file mode 100644 index 000000000..75d4c0e72 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/constant.h @@ -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. + */ + +#ifndef KV_DATASERVICE_CONSTANT_H +#define KV_DATASERVICE_CONSTANT_H + +#include +#include +#include +#include +#include +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreMetaRow { +public: + KVSTORE_API static const std::string KEY_PREFIX; + + KVSTORE_API static std::vector GetKeyFor(const std::string &key); +}; + +class SecretMetaRow { +public: + KVSTORE_API static const std::string KEY_PREFIX; + + KVSTORE_API static std::vector GetKeyFor(const std::string &key); +}; + +class Constant { +public: + // concatenate strings and return a composition string. + KVSTORE_API static std::string Concatenate(std::initializer_list stringList); + + // delete left bland in s by reference. + template + static void LeftTrim(T &s); + + // delete right bland in s by reference. + template + static void RightTrim(T &s); + + // delete both left and right bland in s by reference. + template + static void Trim(T &s); + + // delete left bland in s by reference, not change raw string. + template + static T LeftTrimCopy(T s); + + // delete right bland in s by reference, not change raw string. + template + static T RightTrimCopy(T s); + + // delete both left and right bland in s by reference, not change raw string. + template + static T TrimCopy(T s); + + // get default device account id. + KVSTORE_API static std::string GetDefaultDeviceAccountId(); + + // get default harmony account name. + KVSTORE_API static std::string GetDefaultHarmonyAccountName(); + + // default group id for synchronization based on harmony account. + KVSTORE_API static const std::string DEFAULT_GROUP_ID; + + // Indicates whether only storeid are used as hash materials for the DistributedDB path generated. + KVSTORE_API static const bool STOREID_ONLY_FLAG; + + // version for distributed kv data service. + KVSTORE_API static const std::string VERSION; + + // meta name for distributed kv data service. + KVSTORE_API static const std::string META_DIR_NAME; + + // name for distributed kv data service. + KVSTORE_API static const std::string SERVICE_NAME; + + // root path for distributed kv data service. + KVSTORE_API static const std::string ROOT_PATH; + + // root path for distributeddata service and system services. + KVSTORE_API static const std::string ROOT_PATH_DE; + + // root path for self-developed and non-self-developed app. + KVSTORE_API static const std::string ROOT_PATH_CE; + + // the max length for key is 256. + KVSTORE_API static const size_t MAX_KEY_LENGTH; + + // the max length for value is 1M. + KVSTORE_API static const size_t MAX_VALUE_LENGTH; + + // the max length for StoreId is 64. + KVSTORE_API static const size_t MAX_STORE_ID_LENGTH; + + // the max batch for putBatch is 128. + KVSTORE_API static const size_t MAX_BATCH_SIZE; + + // the max capacity for ipc is 800KB. + KVSTORE_API static const size_t MAX_IPC_CAPACITY; + + // service meta db name. + KVSTORE_API static const std::string SERVICE_META_DB_NAME; + + KVSTORE_API static const std::string KEY_SEPARATOR; + + KVSTORE_API static const mode_t DEFAULT_MODE; + + KVSTORE_API static const mode_t DEFAULT_MODE_DIR; + + KVSTORE_API static const mode_t DEFAULT_MODE_FILE; + + KVSTORE_API static const int SWITCH_RAW_DATA_SIZE; + + KVSTORE_API static const int MAX_OPEN_KVSTORES; + + // name for process label (bus name for communication). compatible with HwDDMP + KVSTORE_API static const std::string PROCESS_LABEL; + KVSTORE_API static const std::string ROOT_KEY_GENERATED; +}; + +// trim from start (in place) +template +void Constant::LeftTrim(T &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); +} + +// trim from end (in place) +template +void Constant::RightTrim(T &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); +} + +// trim from both ends (in place) +template +void Constant::Trim(T &s) +{ + LeftTrim(s); + RightTrim(s); +} + +// trim from start (copying) +template +T Constant::LeftTrimCopy(T s) +{ + LeftTrim(s); + return s; +} + +// trim from end (copying) +template +T Constant::RightTrimCopy(T s) +{ + RightTrim(s); + return s; +} + +// trim from both ends (copying) +template +T Constant::TrimCopy(T s) +{ + Trim(s); + return s; +} +} // namespace DistributedKv +} // namespace OHOS +#endif // KV_DATASERVICE_CONSTANT_H + diff --git a/services/distributeddataservice/adapter/include/autils/directory_utils.h b/services/distributeddataservice/adapter/include/autils/directory_utils.h new file mode 100644 index 000000000..ced19ca79 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/directory_utils.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 DIRECTORY_UTILS_H +#define DIRECTORY_UTILS_H + +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class DirectoryUtils { +public: + KVSTORE_API static bool ChangeModeFileOnly(const std::string &path, const mode_t &mode); + + KVSTORE_API static bool ChangeModeDirOnly(const std::string &path, const mode_t &mode); + + KVSTORE_API static bool CreateDirectory(const std::string &path); + +private: + DirectoryUtils() = default; + + ~DirectoryUtils() = default; + + static std::string ExcludeDelimiterAtPathTail(const std::string &path); + + static std::string IncludeDelimiterAtPathTail(const std::string &path); + + // change the mode of the specified file or directory. + static inline bool ChangeMode(const std::string &name, const mode_t &mode) + { + return (chmod(name.c_str(), mode) == 0); + } +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DIRECTORY_UTILS_H \ No newline at end of file diff --git a/services/distributeddataservice/adapter/include/autils/kv_scheduler.h b/services/distributeddataservice/adapter/include/autils/kv_scheduler.h new file mode 100644 index 000000000..cd9186efa --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/kv_scheduler.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_SCHEDULER_H +#define KV_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include "visibility.h" + +namespace OHOS::DistributedKv { +using SchedulerTask = std::map>::iterator; + +class KvScheduler { +public: + KVSTORE_API KvScheduler(); + KVSTORE_API ~KvScheduler(); + // execute task at specific time + KVSTORE_API SchedulerTask At(const std::chrono::system_clock::time_point &time, std::function task); + // execute task periodically with duration + KVSTORE_API void Every(const std::chrono::system_clock::duration interval, std::function task); + // remove task in SchedulerTask + KVSTORE_API void Remove(const SchedulerTask &task); + // execute task periodically with duration after delay + KVSTORE_API void Every(std::chrono::system_clock::duration delay, + std::chrono::system_clock::duration interval, std::function func); + // execute task for some times periodically with duration after delay + KVSTORE_API void Every(int times, std::chrono::system_clock::duration delay, + std::chrono::system_clock::duration interval, std::function func); +private: + void Loop(); + bool isRunning_; + std::multimap> kvTasks_; + std::mutex mutex_; + std::unique_ptr thread_; + std::condition_variable condition_; +}; +} // namespace DistributedKv::OHOS +#endif // KV_SCHEDULER_H diff --git a/services/distributeddataservice/adapter/include/autils/kv_store_task.h b/services/distributeddataservice/adapter/include/autils/kv_store_task.h new file mode 100644 index 000000000..4b298899b --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/kv_store_task.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_TASK_H +#define KV_STORE_TASK_H + +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreTask { +public: + KVSTORE_API ~KvStoreTask() {}; + KVSTORE_API KvStoreTask(std::function lambda); + KVSTORE_API KvStoreTask(std::function lambda, const std::string &taskName); + KVSTORE_API void operator()(); + +private: + std::function task_; + std::string name_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // TASK_H diff --git a/services/distributeddataservice/adapter/include/autils/kv_store_thread_pool.h b/services/distributeddataservice/adapter/include/autils/kv_store_thread_pool.h new file mode 100755 index 000000000..bd0b900ec --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/kv_store_thread_pool.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 KV_STORE_THREAD_POOL_H +#define KV_STORE_THREAD_POOL_H + +#include +#include "kv_store_task.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreThreadPool { +public: + KvStoreThreadPool(KvStoreThreadPool &&) = delete; + KvStoreThreadPool(const KvStoreThreadPool &) = delete; + KvStoreThreadPool &operator=(KvStoreThreadPool &&) = delete; + KvStoreThreadPool &operator=(const KvStoreThreadPool &) = delete; + KVSTORE_API virtual ~KvStoreThreadPool() {}; + + KVSTORE_API static std::shared_ptr GetPool(int poolSize, bool startImmediately = false); + KVSTORE_API virtual void Stop() = 0; + KVSTORE_API virtual bool AddTask(KvStoreTask &&task) = 0; + KVSTORE_API static constexpr int MAX_POOL_SIZE = 64; // the max thread pool size + KVSTORE_API static constexpr int DEFAULT_POOL_SIZE = 8; // the default thread pool size +protected: + KvStoreThreadPool() = default; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // KV_STORE_THREAD_POOL_H diff --git a/services/distributeddataservice/adapter/include/autils/platform_specific.h b/services/distributeddataservice/adapter/include/autils/platform_specific.h new file mode 100644 index 000000000..c98b734b4 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/platform_specific.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 PLATFORM_SPECIFIC_H +#define PLATFORM_SPECIFIC_H + +#if defined _WIN32 + #define OS_WINDOWS +#else + #define OS_OHOS +#endif + +#endif // PLATFORM_SPECIFIC_H \ No newline at end of file diff --git a/services/distributeddataservice/adapter/include/autils/serializable.h b/services/distributeddataservice/adapter/include/autils/serializable.h new file mode 100755 index 000000000..cdefafe3b --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/serializable.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 OHOS_SERIALIZABLE_H +#define OHOS_SERIALIZABLE_H + +#include + +namespace OHOS::DistributedKv { +struct Serializable { + static inline std::string GetVal(const Json::Value &value, const std::string &def) + { + if (value.isNull()) { + return def; + } + return value.asString(); + } + + static inline uint32_t GetVal(const Json::Value &value, const uint32_t def) + { + if (value.isNull()) { + return def; + } + return value.asUInt(); + } + +#ifndef GET_NAME +#define GET_NAME(value) #value +#endif +}; +} + +#endif // OHOS_SERIALIZABLE_H diff --git a/services/distributeddataservice/adapter/include/autils/time_utils.h b/services/distributeddataservice/adapter/include/autils/time_utils.h new file mode 100755 index 000000000..bfa87ba77 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/time_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 TIME_UTILS_H +#define TIME_UTILS_H + +#include +#include + +namespace OHOS { +namespace DistributedKv { +constexpr int64_t SEC_TO_MICROSEC = 1000000; + +class TimeUtils final { +public: + // micro seconds since 1970 + static inline uint64_t CurrentTimeMicros() + { + struct timeval tv = { 0, 0 }; + gettimeofday(&tv, nullptr); + return (tv.tv_sec * SEC_TO_MICROSEC + tv.tv_usec); + } +}; +} // namespace DistributedKv +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/distributeddataservice/adapter/include/autils/visibility.h b/services/distributeddataservice/adapter/include/autils/visibility.h new file mode 100644 index 000000000..10bc02f92 --- /dev/null +++ b/services/distributeddataservice/adapter/include/autils/visibility.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif + diff --git a/services/distributeddataservice/adapter/include/broadcaster/broadcast_sender.h b/services/distributeddataservice/adapter/include/broadcaster/broadcast_sender.h new file mode 100644 index 000000000..cefe50f79 --- /dev/null +++ b/services/distributeddataservice/adapter/include/broadcaster/broadcast_sender.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 BROADCAST_SENDER_H +#define BROADCAST_SENDER_H + +#include +#include +#include "visibility.h" + +namespace OHOS::DistributedKv { +struct EventParams { + std::string userId; + std::string appId; + std::string storeId; +}; + +class BroadcastSender { +public: + KVSTORE_API virtual void SendEvent(const EventParams ¶ms) = 0; + KVSTORE_API virtual ~BroadcastSender() {}; + KVSTORE_API static std::shared_ptr GetInstance(); +private: + static std::mutex mutex_; + static std::shared_ptr instance_; +}; +} +#endif // BROADCAST_SENDER_H diff --git a/services/distributeddataservice/adapter/include/broadcaster/visibility.h b/services/distributeddataservice/adapter/include/broadcaster/visibility.h new file mode 100644 index 000000000..10bc02f92 --- /dev/null +++ b/services/distributeddataservice/adapter/include/broadcaster/visibility.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif + diff --git a/services/distributeddataservice/adapter/include/communicator/app_data_change_listener.h b/services/distributeddataservice/adapter/include/communicator/app_data_change_listener.h new file mode 100644 index 000000000..b9c261bc6 --- /dev/null +++ b/services/distributeddataservice/adapter/include/communicator/app_data_change_listener.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 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 AppDistributedKv { +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 AppDistributedKv +} // namespace OHOS +#endif // APP_DISTRIBUTEDDATA_INCLUDE_DATA_CHANGE_LISTENER_H diff --git a/services/distributeddataservice/adapter/include/communicator/communication_provider.h b/services/distributeddataservice/adapter/include/communicator/communication_provider.h new file mode 100644 index 000000000..3422a7a42 --- /dev/null +++ b/services/distributeddataservice/adapter/include/communicator/communication_provider.h @@ -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. + */ + +#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 "idevice_query.h" +#include "visibility.h" +namespace OHOS { +namespace AppDistributedKv { +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(); + + KVSTORE_API static std::shared_ptr MakeCommunicationProvider(); + + // check peer device pipeInfo Process + KVSTORE_API virtual bool IsSameStartedOnPeer(const PipeInfo &pipeInfo, const DeviceId &peer) const = 0; + + KVSTORE_API virtual void SetDeviceQuery(std::shared_ptr deviceQuery) = 0; + KVSTORE_API virtual std::string GetUuidByNodeId(const std::string &nodeId) const = 0; + KVSTORE_API virtual std::string GetUdidByNodeId(const std::string &nodeId) const = 0; + KVSTORE_API virtual DeviceInfo GetLocalBasicInfo() const = 0; + KVSTORE_API virtual std::vector GetRemoteNodesBasicInfo() const = 0; + KVSTORE_API virtual std::string ToNodeId(const std::string &id) const = 0; + + KVSTORE_API virtual void SetMessageTransFlag(const PipeInfo &pipeInfo, bool flag) = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATA_COMMUNICATION_PROVIDER_H diff --git a/services/distributeddataservice/adapter/include/communicator/idevice_query.h b/services/distributeddataservice/adapter/include/communicator/idevice_query.h new file mode 100644 index 000000000..118f81845 --- /dev/null +++ b/services/distributeddataservice/adapter/include/communicator/idevice_query.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 IDEVICE_QUERY_H +#define IDEVICE_QUERY_H + +#include "app_types.h" +#include "visibility.h" +namespace OHOS { +namespace AppDistributedKv { +class IDeviceQuery { +public: + KVSTORE_API virtual ~IDeviceQuery() {}; + + // Get online deviceList + KVSTORE_API virtual std::vector GetDeviceList() const = 0; + + // Get local device information + KVSTORE_API virtual DeviceInfo GetLocalDevice() const = 0; +}; +} // namespace AppDistributedKv +} // namespace OHOS +#endif // IDEVICE_QUERY_H diff --git a/services/distributeddataservice/adapter/include/communicator/process_communicator_impl.h b/services/distributeddataservice/adapter/include/communicator/process_communicator_impl.h new file mode 100755 index 000000000..9cf038113 --- /dev/null +++ b/services/distributeddataservice/adapter/include/communicator/process_communicator_impl.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 PROCESS_COMMUNICATOR_IMPL_H +#define PROCESS_COMMUNICATOR_IMPL_H + +#include +#include "iprocess_communicator.h" +#include "communication_provider.h" + +namespace OHOS { +namespace AppDistributedKv { +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 = 5242800; // the max transmission unit size(5MB - 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 AppDistributedKv +} // namespace OHOS +#endif // PROCESS_COMMUNICATOR_IMPL_H diff --git a/services/distributeddataservice/adapter/include/dfx/db_meta_callback_delegate.h b/services/distributeddataservice/adapter/include/dfx/db_meta_callback_delegate.h new file mode 100755 index 000000000..8f4f6622f --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/db_meta_callback_delegate.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 DISTRIBUTEDDATAMGR_CAL_DATABASE_SIZE_H +#define DISTRIBUTEDDATAMGR_CAL_DATABASE_SIZE_H + +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +struct StoreInfo { + std::string userId; + std::string appId; + std::string storeId; + std::string GetKey() + { + return userId + appId + storeId; + } +}; +class DbMetaCallbackDelegate { +public: + KVSTORE_API virtual ~DbMetaCallbackDelegate() {} + KVSTORE_API virtual bool GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) = 0; + KVSTORE_API virtual void GetKvStoreKeys(std::vector &entries) = 0; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_CAL_DATABASE_SIZE_H diff --git a/services/distributeddataservice/adapter/include/dfx/dds_trace.h b/services/distributeddataservice/adapter/include/dfx/dds_trace.h new file mode 100755 index 000000000..f78d50c1e --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/dds_trace.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 DDS_TRACE_H +#define DDS_TRACE_H + +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class DdsTrace { +public: + KVSTORE_API DdsTrace(const std::string &value, bool isSend = false); + KVSTORE_API ~DdsTrace(); + KVSTORE_API void SetMiddleTrace(const std::string &beforeValue, const std::string &afterValue); + +private: + void Start(const std::string &value); + void Middle(const std::string &beforeValue, const std::string &afterValue); + void Finish(const std::string &value); + bool SetBytraceEnable(); + + enum SwitchOption { + DEBUG_CLOSE = 0x00, + BYTRACE_ON = 0x01, + API_PERFORMANCE_TRACE_ON = 0x02, + }; + static std::atomic_uint switchOption; + static std::atomic_uint indexCount; + static std::atomic_bool isSetBytraceEnabled; + std::string traceValue{ }; + uint64_t lastTime{ 0 }; + uint32_t traceCount{ 0 }; + bool isSendToHiview{ false }; +}; +} // namespace OHOS +} // namespace DistributedKv +#endif diff --git a/services/distributeddataservice/adapter/include/dfx/dfx_types.h b/services/distributeddataservice/adapter/include/dfx/dfx_types.h new file mode 100755 index 000000000..1b768f3c1 --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/dfx_types.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_DFX_TYPES_H +#define DISTRIBUTEDDATAMGR_DFX_TYPES_H + +#include +#include +#include "db_meta_callback_delegate.h" + +namespace OHOS { +namespace DistributedKv { +struct ModuleName { + static const inline std::string DEVICE = "DEVICE"; + static const inline std::string USER = "USER"; +}; + +enum class Fault { + // Service Fault + SF_SERVICE_PUBLISH = 0, + SF_GET_SERVICE = 1, + SF_CREATE_DIR = 2, + SF_DATABASE_CONFIG = 3, + SF_PROCESS_LABEL = 4, + SF_INIT_DELEGATE = 5, + + // Runtime Fault + RF_OPEN_DB = 20, + RF_CLOSE_DB = 21, + RF_DELETE_DB = 22, + RF_DATA_SUBSCRIBE = 23, + RF_DATA_UNSUBSCRIBE = 24, + RF_CREATE_SNAPSHOT = 25, + RF_RELEASE_SNAPSHOT = 26, + RF_KEY_STORE_DAMAGE = 27, + RF_KEY_STORE_LOST = 28, + RF_DB_ROM = 29, + RF_GET_DB = 30, + RF_SUBCRIBE = 31, + RF_UNSUBCRIBE = 32, + RF_REPORT_THERAD_ERROR = 33, + + // Communication Fault + CF_CREATE_SESSION = 40, + CF_CREATE_PIPE = 41, + CF_SEND_PIPE_BROKEN = 42, + CF_REV_PIPE_BROKEN = 43, + CF_SERVICE_DIED = 44, + CF_CREATE_ZDN = 45, + CF_QUERY_ZDN = 46, + + // Database Fault + DF_DB_DAMAGE = 60, +}; + +enum class FaultType { + SERVICE_FAULT = 0, + RUNTIME_FAULT = 1, + DATABASE_FAULT = 2, + COMM_FAULT = 3, +}; + +struct FaultMsg { + FaultType faultType; + std::string moduleName; + std::string interfaceName; + Fault errorType; +}; + +struct VisitStat { + std::string appId; + std::string interfaceName; + KVSTORE_API std::string GetKey() const + { + return appId + interfaceName; + } +}; + +struct TrafficStat { + std::string appId; + std::string deviceId; + int sendSize; + int receivedSize; + KVSTORE_API std::string GetKey() const + { + return appId + deviceId; + } +}; + +struct DbStat { + std::string userId; + std::string appId; + std::string storeId; + int dbSize; + std::shared_ptr delegate; + + KVSTORE_API std::string GetKey() const + { + return userId + appId + storeId; + } +}; + +struct DbPerformanceStat { + std::string appId; + std::string storeId; + uint64_t currentTime; + uint64_t completeTime; + int size; + std::string srcDevId; + std::string dstDevId; +}; + +struct ApiPerformanceStat { + std::string interfaceName; + uint64_t costTime; + uint64_t averageTime; + uint64_t worstTime; + KVSTORE_API std::string GetKey() const + { + return interfaceName; + } +}; + +enum class ReportStatus { + SUCCESS = 0, + ERROR = 1, +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_DFX_TYPES_H diff --git a/services/distributeddataservice/adapter/include/dfx/fault_reporter.h b/services/distributeddataservice/adapter/include/dfx/fault_reporter.h new file mode 100644 index 000000000..aa9c4abc4 --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/fault_reporter.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 DISTRIBUTEDDATAMGR_FAULT_REPORTER_H +#define DISTRIBUTEDDATAMGR_FAULT_REPORTER_H + +#include "dfx_types.h" + +namespace OHOS { +namespace DistributedKv { +class FaultReporter { +public: + KVSTORE_API virtual ReportStatus Report(const FaultMsg &msg) = 0; + KVSTORE_API virtual ~FaultReporter() {} +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_FAULT_REPORTER_H diff --git a/services/distributeddataservice/adapter/include/dfx/reporter.h b/services/distributeddataservice/adapter/include/dfx/reporter.h new file mode 100755 index 000000000..79a45c194 --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/reporter.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 DISTRIBUTEDDATAMGR_REPORTER_H +#define DISTRIBUTEDDATAMGR_REPORTER_H + +#include +#include +#include "dfx_types.h" +#include "statistic_reporter.h" +#include "fault_reporter.h" + +namespace OHOS { +namespace DistributedKv { +class Reporter { +public: + KVSTORE_API static Reporter* GetInstance(); + KVSTORE_API FaultReporter* ServiceFault(); + KVSTORE_API FaultReporter* RuntimeFault(); + KVSTORE_API FaultReporter* DatabaseFault(); + KVSTORE_API FaultReporter* CommunicationFault(); + + KVSTORE_API StatisticReporter* DatabaseStatistic(); + KVSTORE_API StatisticReporter* VisitStatistic(); + KVSTORE_API StatisticReporter* TrafficStatistic(); + KVSTORE_API StatisticReporter* ApiPerformanceStatistic(); +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_REPORTER_H diff --git a/services/distributeddataservice/adapter/include/dfx/statistic_reporter.h b/services/distributeddataservice/adapter/include/dfx/statistic_reporter.h new file mode 100644 index 000000000..c05f02be3 --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/statistic_reporter.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 DISTRIBUTEDDATAMGR_STATISTIC_REPORTER_H +#define DISTRIBUTEDDATAMGR_STATISTIC_REPORTER_H + +#include "dfx_types.h" + +namespace OHOS { +namespace DistributedKv { +template +class StatisticReporter { +public: + KVSTORE_API virtual ReportStatus Report(const T &stat) = 0; + KVSTORE_API virtual ~StatisticReporter() {} +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // DISTRIBUTEDDATAMGR_STATISTIC_REPORTER_H diff --git a/services/distributeddataservice/adapter/include/dfx/visibility.h b/services/distributeddataservice/adapter/include/dfx/visibility.h new file mode 100644 index 000000000..10bc02f92 --- /dev/null +++ b/services/distributeddataservice/adapter/include/dfx/visibility.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif + diff --git a/services/distributeddataservice/adapter/include/log/log_print.h b/services/distributeddataservice/adapter/include/log/log_print.h new file mode 100755 index 000000000..ee71fd78a --- /dev/null +++ b/services/distributeddataservice/adapter/include/log/log_print.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 DISTRIBUTEDDATA_LOG_PRINT_H +#define DISTRIBUTEDDATA_LOG_PRINT_H + +#ifndef KVSTORE_API +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif +#define OS_OHOS +#if defined OS_OHOS // log for OHOS + +#include "hilog/log.h" +namespace OHOS { +namespace DistributedKv { + +KVSTORE_API static constexpr OHOS::HiviewDFX::HiLogLabel LOG_LABEL = { LOG_CORE, 0xD001610, "ZDDS" }; + +} // end namespace DistributesdKv + +namespace AppDistributedKv { + +KVSTORE_API static constexpr OHOS::HiviewDFX::HiLogLabel LOG_LABEL = { LOG_CORE, 0xD001620, "ZDDC" }; + +} // end namespace AppDistributesdKv +} // end namespace OHOS + +#define ZLOGD(fmt, ...) \ + OHOS::HiviewDFX::HiLog::Debug(LOG_LABEL, LOG_TAG "::%{public}s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define ZLOGI(fmt, ...) \ + OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, LOG_TAG "::%{public}s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define ZLOGW(fmt, ...) \ + OHOS::HiviewDFX::HiLog::Warn(LOG_LABEL, LOG_TAG "::%{public}s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define ZLOGE(fmt, ...) \ + OHOS::HiviewDFX::HiLog::Error(LOG_LABEL, LOG_TAG "::%{public}s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#else + #error // unknown system +#endif + +#endif // DISTRIBUTEDDATA_LOG_PRINT_H diff --git a/services/distributeddataservice/adapter/include/log/visibility.h b/services/distributeddataservice/adapter/include/log/visibility.h new file mode 100644 index 000000000..10bc02f92 --- /dev/null +++ b/services/distributeddataservice/adapter/include/log/visibility.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif + diff --git a/services/distributeddataservice/adapter/include/permission/permission_validator.h b/services/distributeddataservice/adapter/include/permission/permission_validator.h new file mode 100755 index 000000000..99c6fc126 --- /dev/null +++ b/services/distributeddataservice/adapter/include/permission/permission_validator.h @@ -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. + */ + +#ifndef PERMISSION_VALIDATOR_H +#define PERMISSION_VALIDATOR_H +#include +#include +#include "types.h" +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +// Compare function for kvStoreTupleMap_. +struct KvStoreTupleCmp { + bool operator()(const KvStoreTuple &lKey, const KvStoreTuple &rKey) const + { + if (lKey.userId != rKey.userId) { + return lKey.userId < rKey.userId; + } + if (lKey.appId != rKey.appId) { + return lKey.appId < rKey.appId; + } + if (lKey.storeId != rKey.storeId) { + return lKey.storeId < rKey.storeId; + } + + return false; + } +}; + +class PermissionValidator { +public: + // check whether the client process have enough privilege to share data with the other devices. + // uid: client process uid + KVSTORE_API static bool CheckSyncPermission(const std::string &userId, const std::string &appId, + std::int32_t uid = 0); + + KVSTORE_API static bool RegisterPermissionChanged( + const KvStoreTuple &kvStoreTuple, const AppThreadInfo &appThreadInfo); + + KVSTORE_API static void UnregisterPermissionChanged(const KvStoreTuple &kvStoreTuple); + + KVSTORE_API static void UpdateKvStoreTupleMap(const KvStoreTuple &srcKvStoreTuple, + const KvStoreTuple &dstKvStoreTuple); + + // Check whether the bundle name is in the system service list. + KVSTORE_API static bool IsSystemService(const std::string &bundleName); + + // Check whether the app with this bundle name is auto launch enabled. + KVSTORE_API static bool IsAutoLaunchEnabled(const std::string &bundleName); + +private: + static std::set systemServiceList_; // the full list for system services. + static std::set autoLaunchEnableList_; // the list for auto launch enabled app. +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // PERMISSION_VALIDATOR_H diff --git a/services/distributeddataservice/adapter/include/permission/visibility.h b/services/distributeddataservice/adapter/include/permission/visibility.h new file mode 100644 index 000000000..af8fa9b3d --- /dev/null +++ b/services/distributeddataservice/adapter/include/permission/visibility.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#define KVSTORE_API __attribute__ ((visibility ("default"))) +#endif diff --git a/services/distributeddataservice/adapter/include/security/security_adapter.h b/services/distributeddataservice/adapter/include/security/security_adapter.h new file mode 100755 index 000000000..08ab9d4d9 --- /dev/null +++ b/services/distributeddataservice/adapter/include/security/security_adapter.h @@ -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. + */ + +#ifndef OHOS_SECURITY_ADAPTER_H +#define OHOS_SECURITY_ADAPTER_H + +#include "visibility.h" +namespace OHOS::DistributedKv { +KVSTORE_API void InitSecurityAdapter(); +} +#endif // OHOS_SECURITY_ADAPTER_H diff --git a/services/distributeddataservice/adapter/include/utils/crypto_utils.h b/services/distributeddataservice/adapter/include/utils/crypto_utils.h new file mode 100644 index 000000000..1595115b4 --- /dev/null +++ b/services/distributeddataservice/adapter/include/utils/crypto_utils.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 CRYPTO_UTILS_H +#define CRYPTO_UTILS_H + +#include +#include +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +class CryptoUtils { +public: +// get sha256 hash +// plainText: string to be hashed +// sha256 hash of the input string +KVSTORE_API static std::string Sha256(const std::string &plainText); + +// get appId from bundle name +// bundleName: client declared bundleName +// userId: id of current user +// hashed id of this app. each char in returned string minus 'A' is the sha512 value of origin appId. +KVSTORE_API static std::string Sha256AppId(const std::string &bundleName, int userId = 0); + +// get sha256 hash +// plainText: string to be hashed +// sha256 hash of the input string +KVSTORE_API static std::string Sha256UserId(const std::string &plainText); + +// Use system entropy pool to generate true random number. note that it will run quiet slowly on system entropy +// pool running out. +// keyLen: returned vector length. +// true random 8-bit sequence. +KVSTORE_API static void GetRandomKey(int keyLen, std::vector &key); +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // CRYPTO_UTILS_H \ No newline at end of file diff --git a/services/distributeddataservice/adapter/include/utils/json_utils.h b/services/distributeddataservice/adapter/include/utils/json_utils.h new file mode 100755 index 000000000..cb706b565 --- /dev/null +++ b/services/distributeddataservice/adapter/include/utils/json_utils.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 JSON_UTILS_H +#define JSON_UTILS_H + +#include +#include +#include "log_print.h" +#include "visibility.h" + +namespace OHOS { +namespace DistributedKv { +using json = nlohmann::json; + +class JsonUtils { +public: +template +static bool ParseJsonField(const std::string &jsonStr, const std::string &key, uint8_t valType, T &fieldVal) +{ + json jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + ZLOGE("jsonObj is discarded, parse json[%s] failed.", key.c_str()); + return false; + } + if (jsonObj.find(key) == jsonObj.end()) { + return false; + } + if (static_cast(jsonObj[key].type()) != valType) { + ZLOGE("not match in jsonObj, parse json[%s] failed.", key.c_str()); + return false; + } + jsonObj[key].get_to(fieldVal); + return true; +} + +template +static bool ParseJsonField(const json &jsonObj, const std::string &key, uint8_t valType, T &fieldVal) +{ + if (jsonObj.is_discarded()) { + ZLOGE("jsonObj is discarded, parse json[%s] failed.", key.c_str()); + return false; + } + if (jsonObj.find(key) == jsonObj.end()) { + return false; + } + if (static_cast(jsonObj[key].type()) != valType) { + ZLOGE("not match in jsonObj, parse json[%s] failed.", key.c_str()); + return false; + } + jsonObj[key].get_to(fieldVal); + return true; +} +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // JSON_UTILS_H \ No newline at end of file diff --git a/services/distributeddataservice/adapter/include/utils/kvstore_utils.h b/services/distributeddataservice/adapter/include/utils/kvstore_utils.h new file mode 100755 index 000000000..7a5512156 --- /dev/null +++ b/services/distributeddataservice/adapter/include/utils/kvstore_utils.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 KVSTORE_UTILS_H +#define KVSTORE_UTILS_H + +#include +#include "visibility.h" +#include "communication_provider.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreUtils { +public: + // get app id; KvStore may use BundleName as appId, this function should run in IPC thread + // bundleName: string to be hashed + KVSTORE_API static std::string GetAppIdByBundleName(const std::string &bundleName); + + // convert the name to the anonymous + // the anonymous string is name[0,3]"***"name[end-3, end] + // when the anonymous string is shorter than 9, the anonymous is "******" + KVSTORE_API static std::string ToBeAnonymous(const std::string &name); + + KVSTORE_API static AppDistributedKv::CommunicationProvider &GetProviderInstance(); +private: + static constexpr int MAIN_USER_ID = 0; + static constexpr int SYSTEM_UID = 1000; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_UTILS_H diff --git a/services/distributeddataservice/adapter/permission/BUILD.gn b/services/distributeddataservice/adapter/permission/BUILD.gn new file mode 100755 index 000000000..5cbfb8273 --- /dev/null +++ b/services/distributeddataservice/adapter/permission/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_permission_static") { + sources = [ + "src/client_permission_validator.cpp", + "src/permission_validator.cpp", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + include_dirs = [ + "../include/permission", + "../include/utils", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//third_party/openssl/include/", + ] + + if (build_public_version) { + cflags_cc += [ "-DCONFIG_PUBLIC_VERSION" ] + } + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "../utils:distributeddata_utils_static", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + + external_deps = [ + "aafwk_standard:base", + "aafwk_standard:intent", + "appexecfwk_standard:appexecfwk_base", + "appexecfwk_standard:appexecfwk_core", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + ] +} diff --git a/services/distributeddataservice/adapter/permission/src/client_permission_validator.cpp b/services/distributeddataservice/adapter/permission/src/client_permission_validator.cpp new file mode 100755 index 000000000..0c8390ef2 --- /dev/null +++ b/services/distributeddataservice/adapter/permission/src/client_permission_validator.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. + */ + +#define LOG_TAG "ClientPermissionValidator" + +#include "client_permission_validator.h" +#include +#include + +namespace OHOS { +namespace DistributedKv { + +void ClientPermissionValidator::UpdatePermissionStatus( + int32_t uid, const std::string &permissionType, bool permissionStatus) +{ + if (permissionType == DISTRIBUTED_DATASYNC) { + std::lock_guard permissionLock(permissionMutex_); + dataSyncPermissionMap_[uid] = permissionStatus; + } +} + +bool ClientPermissionValidator::CheckClientSyncPermission(const KvStoreTuple &kvStoreTuple, std::int32_t curUid) +{ + return true; +} + +bool ClientPermissionValidator::RegisterPermissionChanged( + const KvStoreTuple &kvStoreTuple, const AppThreadInfo &appThreadInfo) +{ + return false; +} + +void ClientPermissionValidator::UnregisterPermissionChanged(const KvStoreTuple &kvStoreTuple) +{ + +} + +// after the BMS service restarted, rebuild BundleManager object and re-register permission callbacks for all apps. +void ClientPermissionValidator::RebuildBundleManager() +{ + +} + +void ClientPermissionValidator::UpdateKvStoreTupleMap(const KvStoreTuple &srcKvStoreTuple, + const KvStoreTuple &dstKvStoreTuple) +{ + +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/permission/src/client_permission_validator.h b/services/distributeddataservice/adapter/permission/src/client_permission_validator.h new file mode 100755 index 000000000..eaece3cd6 --- /dev/null +++ b/services/distributeddataservice/adapter/permission/src/client_permission_validator.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 CLIENT_PERMISSION_VALIDATOR_H +#define CLIENT_PERMISSION_VALIDATOR_H + +#include "permission_validator.h" +#include +#include +#include + +namespace OHOS { +namespace DistributedKv { + +const std::string DISTRIBUTED_DATASYNC = "ohos.permission.DISTRIBUTED_DATASYNC"; + +class ClientPermissionValidator { +public: + static ClientPermissionValidator &GetInstance() + { + static ClientPermissionValidator clientPermissionValidator; + return clientPermissionValidator; + } + + bool RegisterPermissionChanged(const KvStoreTuple &kvStoreTuple, const AppThreadInfo &appThreadInfo); + + void UnregisterPermissionChanged(const KvStoreTuple &kvStoreTuple); + + void UpdateKvStoreTupleMap(const KvStoreTuple &srcKvStoreTuple, const KvStoreTuple &dstKvStoreTuple); + + void UpdatePermissionStatus(int32_t uid, const std::string &permissionType, bool permissionStatus); + + bool CheckClientSyncPermission(const KvStoreTuple &kvStoreTuple, std::int32_t curUid); + +private: + ClientPermissionValidator() = default; + + ~ClientPermissionValidator() = default; + + ClientPermissionValidator(const ClientPermissionValidator &clientPermissionValidator); + + const ClientPermissionValidator &operator=(const ClientPermissionValidator &clientPermissionValidator); + + void RebuildBundleManager(); + + std::mutex tupleMutex_; + std::mutex permissionMutex_; + std::map dataSyncPermissionMap_; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // CLIENT_PERMISSION_VALIDATOR_H diff --git a/services/distributeddataservice/adapter/permission/src/permission_validator.cpp b/services/distributeddataservice/adapter/permission/src/permission_validator.cpp new file mode 100755 index 000000000..575f1b7f4 --- /dev/null +++ b/services/distributeddataservice/adapter/permission/src/permission_validator.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PermissionValidator" + +#include "permission_validator.h" + +#include +#include +#include "client_permission_validator.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +// initialize system service full list. +std::set PermissionValidator::systemServiceList_ = { + "bundle_manager_service", // BMS + "ivi_config_manager", // IVI + "form_storage" // form +}; + +// initialize auto launch enabled applications white list. +std::set PermissionValidator::autoLaunchEnableList_ = { + "providers.calendar", + "com.huawei.contacts.sync", + "com.huawei.ohos.totemweather" +}; + +// check whether the client process have enough privilege to share data with the other devices. +bool PermissionValidator::CheckSyncPermission(const std::string &userId, const std::string &appId, std::int32_t uid) +{ + KvStoreTuple kvStoreTuple {userId, appId}; + return ClientPermissionValidator::GetInstance().CheckClientSyncPermission(kvStoreTuple, uid); +} + +bool PermissionValidator::RegisterPermissionChanged( + const KvStoreTuple &kvStoreTuple, const AppThreadInfo &appThreadInfo) +{ + return ClientPermissionValidator::GetInstance().RegisterPermissionChanged(kvStoreTuple, appThreadInfo); +} + +void PermissionValidator::UnregisterPermissionChanged(const KvStoreTuple &kvStoreTuple) +{ + return ClientPermissionValidator::GetInstance().UnregisterPermissionChanged(kvStoreTuple); +} + +void PermissionValidator::UpdateKvStoreTupleMap(const KvStoreTuple &srcKvStoreTuple, + const KvStoreTuple &dstKvStoreTuple) +{ + return ClientPermissionValidator::GetInstance().UpdateKvStoreTupleMap(srcKvStoreTuple, dstKvStoreTuple); +} + + +// Check whether the bundle name is in the system service list. +bool PermissionValidator::IsSystemService(const std::string &bundleName) +{ + auto it = systemServiceList_.find(bundleName); + if (it == systemServiceList_.end()) { + ZLOGD("bundleName:%s is not system service.", bundleName.c_str()); + return false; + } + ZLOGD("bundleName:%s is system service.", bundleName.c_str()); + return true; +} + +// Check whether the app with this bundle name is auto launch enabled. +bool PermissionValidator::IsAutoLaunchEnabled(const std::string &bundleName) +{ + for (auto it : autoLaunchEnableList_) { + size_t pos = bundleName.rfind(it); + if (pos != std::string::npos) { + return true; + } + } + ZLOGD("AppId:%s is not allowed.", bundleName.c_str()); + return false; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/permission/test/BUILD.gn b/services/distributeddataservice/adapter/permission/test/BUILD.gn new file mode 100755 index 000000000..c91f7cfe9 --- /dev/null +++ b/services/distributeddataservice/adapter/permission/test/BUILD.gn @@ -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. +import("//build/test.gni") + +module_output_path = "distributeddatamgr/distributeddatafwk" + +############################################################################### +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ + "../../include/permission/", + "../../include/utils/", + "//utils/native/base/include/", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + ] +} + +ohos_unittest("PermissionValidatorTest") { + module_out_path = module_output_path + + sources = [ "unittest/permission_validator_test.cpp" ] + + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//third_party/googletest:gtest_main", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + ] +} + +group("unittest") { + testonly = true + + deps = [] + + deps += [ ":PermissionValidatorTest" ] +} +############################################################################### diff --git a/services/distributeddataservice/adapter/permission/test/unittest/permission_validator_test.cpp b/services/distributeddataservice/adapter/permission/test/unittest/permission_validator_test.cpp new file mode 100755 index 000000000..6fdfab00f --- /dev/null +++ b/services/distributeddataservice/adapter/permission/test/unittest/permission_validator_test.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 + +#include "crypto_utils.h" +#include "permission_validator.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class PermissionValidatorTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void PermissionValidatorTest::SetUpTestCase(void) +{} + +void PermissionValidatorTest::TearDownTestCase(void) +{} + +void PermissionValidatorTest::SetUp(void) +{} + +void PermissionValidatorTest::TearDown(void) +{} + +/** + * @tc.name: TestPermissionValidate001 + * @tc.desc: test if CheckPermission can return correct permission. + * @tc.type: FUNC + * @tc.require: AR000CQDUT + * @tc.author: liqiao + */ +HWTEST_F(PermissionValidatorTest, TestPermissionValidate001, TestSize.Level0) +{ + std::string userId = "ohos"; + std::string appId = "ohosApp"; + EXPECT_TRUE(PermissionValidator::CheckSyncPermission(userId, appId)); +} + +/** + * @tc.name: TestPermissionValidate002 + * @tc.desc: test if CheckPermission can return correct permission. + * @tc.type: FUNC + * @tc.require:AR000DPSGU + * @tc.author: liqiao + */ +HWTEST_F(PermissionValidatorTest, TestPermissionValidate002, TestSize.Level0) +{ + std::string userId = "ohos"; + std::string appId = "ohosApp"; + EXPECT_TRUE(PermissionValidator::CheckSyncPermission(userId, appId)); +} + +/** + * @tc.name: TestPermissionValidate003 + * @tc.desc: test if account id sha256. + * @tc.type: FUNC + * @tc.require: AR000DPSH0 AR000DPSEC + * @tc.author: liqiao + */ +HWTEST_F(PermissionValidatorTest, TestPermissionValidate003, TestSize.Level0) +{ + std::string userId = "ohos"; + EXPECT_NE(CryptoUtils::Sha256("ohos"), userId); +} diff --git a/services/distributeddataservice/adapter/security/BUILD.gn b/services/distributeddataservice/adapter/security/BUILD.gn new file mode 100755 index 000000000..b60a7bf1e --- /dev/null +++ b/services/distributeddataservice/adapter/security/BUILD.gn @@ -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. + +import("//build/ohos.gni") + +ohos_static_library("distributeddata_security_static") { + sources = [ + "src/block_integer.cpp", + "src/security.cpp", + "src/security_adapter.cpp", + "src/sensitive.cpp", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + remove_configs = [ "//build/config/compiler:no_exceptions" ] + + configs = [ + "//build/config/compiler:exceptions", + "//third_party/jsoncpp:jsoncpp_config", + ] + + include_dirs = [ + "../include/autils", + "../include/log", + "../include/security", + "../include/communicator", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + ] + + deps = [ + "//third_party/jsoncpp:jsoncpp", + "//utils/native/base:utils", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + ] + + external_deps = [ + "dataclassification:fbe_iudf_xattr", + "dataclassification:hwdsl", + "dsoftbus_standard:softbus_client", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "os_account_standard:libaccountkits", + ] +} diff --git a/services/distributeddataservice/adapter/security/src/block_integer.cpp b/services/distributeddataservice/adapter/security/src/block_integer.cpp new file mode 100755 index 000000000..8c800cca8 --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/block_integer.cpp @@ -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. + */ + +#include "block_integer.h" +#include + +BlockInteger &BlockInteger::operator++() +{ + value_++; + usleep(interval_); + return *this; +} + +BlockInteger BlockInteger::operator++(int) +{ + BlockInteger temp(interval_); + temp.value_ = value_; + value_++; + usleep(interval_); + return temp; +} + +BlockInteger &BlockInteger::operator=(int value) +{ + value_ = value; + return *this; +} diff --git a/services/distributeddataservice/adapter/security/src/block_integer.h b/services/distributeddataservice/adapter/security/src/block_integer.h new file mode 100755 index 000000000..62ed8b4db --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/block_integer.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 OHOS_BLOCK_INTEGER_H +#define OHOS_BLOCK_INTEGER_H + +class BlockInteger { +public: + explicit BlockInteger(int interval) + : interval_(interval) + { + }; + BlockInteger(const BlockInteger &integer) + : interval_(integer.interval_), value_(integer.value_) + { + }; + BlockInteger &operator=(const BlockInteger &integer) = default; + + ~BlockInteger() = default; + + operator int () const + { + return value_; + } + bool operator < (int other) const + { + return value_ < other; + } + + BlockInteger &operator=(int value); + + BlockInteger &operator++(); + + BlockInteger operator++(int); +private: + int interval_ = 0; + int value_ = 0; +}; + + +#endif // OHOS_BLOCK_INTEGER_H diff --git a/services/distributeddataservice/adapter/security/src/security.cpp b/services/distributeddataservice/adapter/security/src/security.cpp new file mode 100755 index 000000000..11085c4bf --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/security.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 "security.h" +#include +#include +#include "communication_provider.h" +#include "constant.h" +#include "fbe_sdp_policy.h" +#include "sensitive.h" +#include "log_print.h" +#include "block_integer.h" +#include "ohos_account_kits.h" + +#undef LOG_TAG +#define LOG_TAG "SecurityAdapter" +namespace OHOS::DistributedKv { +using namespace DistributedDB; +std::atomic_bool Security::isInitialized_ = true; +const char * const Security::LABEL_VALUES[S4 + 1] = { + "", LABEL_VALUE_S0, LABEL_VALUE_S1, LABEL_VALUE_S2, LABEL_VALUE_S3, LABEL_VALUE_S4 +}; + +const char * const Security::DATA_DE[] = { + "/data/user_de/", + "/data/misc_de/", + nullptr +}; + +const char * const Security::DATA_CE[] = { + "/storage/emulated/", + "/data/misc_ce/", + "/data/user/", + "/mnt/mdfs/", + nullptr +}; + +Security::Security(const std::string &appId, const std::string &userId, const std::string &dir) + : delegateMgr_(appId, userId) +{ + delegateMgr_.SetKvStoreConfig({dir}); + ZLOGD("constructor kvStore_ is %s", dir.c_str()); +} + +Security::~Security() +{ + ZLOGD("destructor kvStore_ is null.%d", kvStore_ == nullptr); + delegateMgr_.CloseKvStore(kvStore_); + kvStore_ = nullptr; +} + +void Security::InitLocalCertData() const +{ + std::thread th = std::thread([keep = shared_from_this()] { + ZLOGI("Save sensitive to meta db"); + DBStatus status = DB_ERROR; + // retry after 10 second, 10 * 1000 * 1000 mains 1 second + BlockInteger retry(10 * 1000 * 1000); + auto &network = AppDistributedKv::CommunicationProvider::GetInstance(); + for (; retry < RETRY_MAX_TIMES; ++retry) { + auto info = network.GetLocalBasicInfo(); + + Sensitive sensitive(network.GetUdidByNodeId(info.deviceId), 0); + if (!sensitive.LoadData()) { + continue; + } + + if (keep->kvStore_ == nullptr) { + ZLOGE("The kvStore_ is null"); + break; + } + + std::string uuid = network.GetUuidByNodeId(info.deviceId); + status = keep->kvStore_->Put(keep->GenerateSecurityKey(uuid), sensitive.Marshal()); + if (status != OK) { + continue; + } + + keep->SyncMeta(); + break; + } + ZLOGI("Save sensitive finished! retry:%d, status: %d", static_cast(retry), status); + // sleep 1 second to avoid + ++retry; + }); + th.detach(); +} + +void Security::InitKvStore() +{ + kvStore_ = GetMetaKvStore(delegateMgr_); + ZLOGD("Init KvStore ,kvStore_ is null.%d", kvStore_ == nullptr); +} + +DBStatus Security::RegOnAccessControlledEvent(const OnAccessControlledEvent &callback) +{ + ZLOGD("add new lock status observer! current size: %d", static_cast(observers_.size())); + if (callback == nullptr) { + return INVALID_ARGS; + } + + if (!IsSupportSecurity()) { + ZLOGI("Not support lock status!"); + return NOT_SUPPORT; + } + + if (observers_.empty()) { + observers_.insert(std::pair(GetCurrentUserId(), callback)); + std::thread th = std::thread([this] { + ZLOGI("Start to subscribe lock status!"); + bool result = false; + std::function observer = [this](int32_t userId, int32_t state) -> int32_t { + auto observer = observers_.find(userId); + if (observer == observers_.end() || observer->second == nullptr) { + return DB_ERROR; + } + observer->second(!(state == UNLOCK || state == NO_PWD)); + return OK; + }; + // retry after 10 second, 10 * 1000 * 1000 mains 1 second + BlockInteger retry(10 * 1000 * 1000); + for (; retry < RETRY_MAX_TIMES && !result; ++retry) { + result = SubscribeUserStatus(observer); + } + + ZLOGI("Subscribe lock status! retry:%d, result: %d", static_cast(retry), result); + }); + th.detach(); + } else { + observers_.insert(std::pair(GetCurrentUserId(), callback)); + } + return OK; +} + +bool Security::IsAccessControlled() const +{ + int curStatus = GetCurrentUserStatus(); + return !(curStatus == UNLOCK || curStatus == NO_PWD); +} + +DBStatus Security::SetSecurityOption(const std::string &filePath, const SecurityOption &option) +{ + if (filePath.empty()) { + return INVALID_ARGS; + } + + if (!InPathsBox(filePath, DATA_DE) && !InPathsBox(filePath, DATA_CE)) { + return NOT_SUPPORT; + } + + struct stat curStat; + stat(filePath.c_str(), &curStat); + if (S_ISDIR(curStat.st_mode)) { + return SetDirSecurityOption(filePath, option); + } else { + return SetFileSecurityOption(filePath, option); + } +} + +DBStatus Security::GetSecurityOption(const std::string &filePath, SecurityOption &option) const +{ + if (filePath.empty()) { + return INVALID_ARGS; + } + + if (!InPathsBox(filePath, DATA_DE) && !InPathsBox(filePath, DATA_CE)) { + return NOT_SUPPORT; + } + + struct stat curStat; + stat(filePath.c_str(), &curStat); + if (S_ISDIR(curStat.st_mode)) { + return GetDirSecurityOption(filePath, option); + } else { + return GetFileSecurityOption(filePath, option); + } +} + +DBStatus Security::GetDirSecurityOption(const std::string &filePath, SecurityOption &option) const +{ + if (!IsSupportSecurity()) { + option.securityFlag = -1; + return OK; + } + + int policy = GetPathPolicy(filePath.c_str()); + switch (policy) { + case FSCRYPT_SDP_ECE_CLASS: + option.securityFlag = ECE; + break; + case FSCRYPT_SDP_SECE_CLASS: + option.securityFlag = SECE; + break; + default: + option.securityFlag = -1; + break; + } + return OK; +} + +DBStatus Security::GetFileSecurityOption(const std::string &filePath, SecurityOption &option) const +{ + if (!IsExits(filePath)) { + option = {NOT_SET, ECE}; + return OK; + } + + int userId = GetCurrentUserId(); + char value[LABEL_VALUE_LEN]{0}; + int err = GetLabel(userId, filePath.c_str(), LABEL_NAME_SECURITY_LEVEL, value, LABEL_VALUE_LEN); + if (err != RET_SDP_OK && err != RET_SDP_NOT_SET_ERROR && err != RET_SDP_NOT_SUPPORT_ATTR) { + ZLOGE("Get Label failed! error: %d, value: %s path:%s", err, value, filePath.c_str()); + return DB_ERROR; + } + + if (err == RET_SDP_NOT_SUPPORT_ATTR) { + ZLOGD("Not support attr ioctl! value: %s path:%s", value, filePath.c_str()); + return NOT_SUPPORT; + } + + int flag = (err == RET_SDP_OK) ? GetFlag(userId, filePath.c_str(), LABEL_NAME_SECURITY_LEVEL) : ECE; + if (flag == -1) { + ZLOGE("Get Flag failed! error: %d, value: %s path:%s", err, value, filePath.c_str()); + } + + option = {Convert2Security(std::string(value)), flag}; + return OK; +} + +DBStatus Security::SetDirSecurityOption(const std::string &filePath, const SecurityOption &option) +{ + int error = RET_SDP_OK; + switch (option.securityLabel) { + case S3: + case S4: { + if (IsSupportSecurity()) { + int userId = GetCurrentUserId(); + error = SetSecePathPolicy(userId, filePath.c_str()); + } + break; + } + default: + break; + } + if (error != RET_SDP_OK && error != RET_SDP_SUPPORT_IUDF_ERROR) { + ZLOGE("Set path policy failed(%d)! label:%d, flag:%d path:%s", + error, option.securityLabel, option.securityFlag, filePath.c_str()); + return NO_PERMISSION; + } + return OK; +} + +DBStatus Security::SetFileSecurityOption(const std::string &filePath, const SecurityOption &option) +{ + if (option.securityLabel == NOT_SET) { + return OK; + } + + const char *value = Convert2Name(option, !InPathsBox(filePath, DATA_DE)); + if (value == nullptr) { + ZLOGE("Invalid args Label failed! label:%d, flag:%d path:%s", + option.securityLabel, option.securityFlag, filePath.c_str()); + return INVALID_ARGS; + } + + int userId = GetCurrentUserId(); + int err = SetLabel(userId, filePath.c_str(), LABEL_NAME_SECURITY_LEVEL, value, option.securityFlag); + if (err != RET_SDP_OK && err != RET_SDP_LABEL_HAS_BEEN_SET && err != RET_SDP_NOT_SUPPORT_ATTR) { + ZLOGE("Set Label failed! label:%d, flag:%d value:%s path:%s", + option.securityLabel, option.securityFlag, value, filePath.c_str()); + return DB_ERROR; + } + + if (err == RET_SDP_NOT_SUPPORT_ATTR) { + ZLOGD("Not support attr ioctl! value: %s path:%s", value, filePath.c_str()); + return NOT_SUPPORT; + } + + return OK; +} + +bool Security::CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const +{ + if (kvStore_ == nullptr) { + ZLOGD("The kv store is null, label:%d", option.securityLabel); + return GetDeviceNodeByUuid(devId, nullptr) >= option; + } + + auto getValue = [this, &devId, &option]() -> std::vector { + Value value; + DBStatus status = kvStore_->Get(GenerateSecurityKey(devId), value); + if (status != OK) { + ZLOGE("Can't get the peer(%.10s)'s cert key! label:%d", devId.c_str(), option.securityLabel); + return {}; + } + return value; + }; + Sensitive sensitive = GetDeviceNodeByUuid(devId, getValue); + + ZLOGD("Got the chain deviceId:%.10s, label:%d", devId.c_str(), option.securityLabel); + return sensitive >= option; +} + +int32_t Security::GetCurrentUserId() const +{ + std::int32_t uid = getuid(); + return AccountSA::OhosAccountKits::GetInstance().GetDeviceAccountIdByUID(uid); +} + +int32_t Security::GetCurrentUserStatus() const +{ + if (!IsSupportSecurity()) { + return NO_PWD; + } + return GetLockState(GetCurrentUserId(), FLAG_LOCAL_STATE); +} + +bool Security::SubscribeUserStatus(std::function &observer) const +{ + int error = RegisterLockStateChangeCallback(FLAG_LOCAL_STATE, observer); + if (error == RET_LOCK_OK) { + // retroactively the current status + observer(GetCurrentUserId(), GetCurrentUserStatus()); + } + return (error == RET_LOCK_OK); +} + +const char *Security::Convert2Name(const SecurityOption &option, bool isCE) +{ + if (option.securityLabel <= NOT_SET || option.securityLabel > S4) { + return nullptr; + } + + if (isCE && option.securityLabel < S2) { + return nullptr; + } + + if (!isCE && option.securityLabel >= S2) { + return nullptr; + } + + return LABEL_VALUES[option.securityLabel]; +} + +int Security::Convert2Security(const std::string &name) +{ + for (int i = 0; i <= S4; i++) { + if (name == LABEL_VALUES[i]) { + return i; + } + } + return NOT_SET; +} + +KvStoreNbDelegate *Security::GetMetaKvStore(KvStoreDelegateManager &delegateMgr) +{ + KvStoreNbDelegate::Option option; + option.createIfNecessary = true; + option.isMemoryDb = false; + option.createDirByStoreIdOnly = true; + option.isEncryptedDb = false; + KvStoreNbDelegate *delegate = nullptr; + delegateMgr.GetKvStore( + Constant::SERVICE_META_DB_NAME, option, + [&delegate](DBStatus status, KvStoreNbDelegate *kvStore) { + if (kvStore != nullptr) { + delegate = kvStore; + } + (void)status; + }); + return delegate; +} + +std::vector Security::GenerateSecurityKey(const std::string &deviceId) const +{ + std::string key = SECURITY_LABEL + Constant::KEY_SEPARATOR + deviceId + Constant::KEY_SEPARATOR + "default"; + return std::vector(key.begin(), key.end()); +} + +void Security::SyncMeta() const +{ + auto &network = AppDistributedKv::CommunicationProvider::GetInstance(); + auto nodeInfos = network.GetRemoteNodesBasicInfo(); + std::vector devices; + for (auto &node : nodeInfos) { + devices.push_back(network.GetUuidByNodeId(node.deviceId)); + } + + kvStore_->Sync(devices, SYNC_MODE_PUSH_ONLY, + [](const std::map &result) { + int count = 0; + for (const auto &[deviceId, status] : result) { + if (status != OK) { + count++; + } + } + if (count > 0) { + ZLOGE("Sync failed(%d), total(%d)!", count, int32_t(result.size())); + } + }); +} + +bool Security::IsSupportSecurity() +{ + return IsSupportIudf(); +} + +bool Security::IsFirstInit() +{ + return isInitialized_.exchange(false); +} + +bool Security::IsExits(const std::string &file) const +{ + return access(file.c_str(), F_OK) == 0; +} + +bool Security::InPathsBox(const std::string &file, const char * const pathsBox[]) const +{ + auto curPath = pathsBox; + if (curPath == nullptr) { + return false; + } + while ((*curPath) != nullptr) { + if (file.find(*curPath) == 0) { + return true; + } + curPath++; + } + return false; +} + +Sensitive Security::GetDeviceNodeByUuid(const std::string &uuid, + const std::function(void)> &getValue) +{ + static std::mutex mutex; + static std::map devicesUdid; + std::lock_guard guard(mutex); + auto it = devicesUdid.find(uuid); + if (devicesUdid.find(uuid) != devicesUdid.end()) { + return it->second; + } + + auto &network = AppDistributedKv::CommunicationProvider::GetInstance(); + auto devices = network.GetRemoteNodesBasicInfo(); + devices.push_back(network.GetLocalBasicInfo()); + for (auto &device : devices) { + auto deviceUuid = network.GetUuidByNodeId(device.deviceId); + ZLOGD("GetDeviceNodeByUuid(%.10s) peer device is %.10s", uuid.c_str(), deviceUuid.c_str()); + if (uuid != deviceUuid) { + continue; + } + + Sensitive sensitive(network.GetUdidByNodeId(device.deviceId), 0); + if (getValue == nullptr) { + devicesUdid.insert({uuid, std::move(sensitive)}); + return devicesUdid[uuid]; + } + + auto value = getValue(); + sensitive.Unmarshal(value); + if (!value.empty()) { + devicesUdid.insert({uuid, std::move(sensitive)}); + return devicesUdid[uuid]; + } + + return sensitive; + } + + return Sensitive(); +} +} diff --git a/services/distributeddataservice/adapter/security/src/security.h b/services/distributeddataservice/adapter/security/src/security.h new file mode 100644 index 000000000..a7baacc37 --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/security.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_SECURITY_H +#define OHOS_SECURITY_H + +#include +#include +#include +#include +#include "iprocess_system_api_adapter.h" +#include "kv_store_delegate_manager.h" +#include "visibility.h" +#include "sensitive.h" + +namespace OHOS::DistributedKv { +class Security + : public DistributedDB::IProcessSystemApiAdapter, public std::enable_shared_from_this { +public: + using DBStatus = DistributedDB::DBStatus; + using OnAccessControlledEvent = DistributedDB::OnAccessControlledEvent; + using SecurityOption = DistributedDB::SecurityOption; + Security(const std::string &appId, const std::string &userId, const std::string &dir); + ~Security() override; + void InitLocalCertData() const; + void InitKvStore(); + + static bool IsFirstInit(); + static bool IsSupportSecurity(); + + DBStatus RegOnAccessControlledEvent(const OnAccessControlledEvent &callback) override; + + // Check is the access of this device in locked state + bool IsAccessControlled() const override; + + // Set the SecurityOption to the targe filepath. + // If the filePath is a directory, All the files and directories in the filePath should be effective. + DBStatus SetSecurityOption(const std::string &filePath, const SecurityOption &option) override; + + // Get the SecurityOption of the targe filepath. + DBStatus GetSecurityOption(const std::string &filePath, SecurityOption &option) const override; + + // Check if the target device can save the data at the give sensitive class. + bool CheckDeviceSecurityAbility(const std::string &devId, const SecurityOption &option) const override; + + static const char *Convert2Name(const SecurityOption &option, bool isCE); + static int Convert2Security(const std::string &name); +private: + enum { + NO_PWD = -1, + UNLOCK, + LOCKED, + UNINITIALIZED, + }; + + // the key is security_chain/{deviceId} + static constexpr const char *SECURITY_LABEL = "SecurityLabel"; + static const char * const LABEL_VALUES[DistributedDB::S4 + 1]; + static const char * const DATA_DE[]; // = "/data/misc_de/", "/data/user_de/"; + static const char * const DATA_CE[]; + static constexpr int LABEL_VALUE_LEN = 10; + static constexpr int RETRY_MAX_TIMES = 10; + int32_t GetCurrentUserId() const; + int32_t GetCurrentUserStatus() const; + bool SubscribeUserStatus(std::function &observer) const; + static DistributedDB::KvStoreNbDelegate *GetMetaKvStore(DistributedDB::KvStoreDelegateManager &delegateMgr); + bool IsExits(const std::string &file) const; + bool InPathsBox(const std::string &file, const char * const pathsBox[]) const; + std::vector GenerateSecurityKey(const std::string &deviceId) const; + void SyncMeta() const; + static Sensitive GetDeviceNodeByUuid(const std::string &uuid, + const std::function(void)> &getValue); + DBStatus GetDirSecurityOption(const std::string &filePath, SecurityOption &option) const; + DBStatus GetFileSecurityOption(const std::string &filePath, SecurityOption &option) const; + DBStatus SetDirSecurityOption(const std::string &filePath, const SecurityOption &option); + DBStatus SetFileSecurityOption(const std::string &filePath, const SecurityOption &option); + + std::map observers_ { }; + DistributedDB::KvStoreDelegateManager delegateMgr_; + DistributedDB::KvStoreNbDelegate *kvStore_ = nullptr; + static std::atomic_bool isInitialized_; +}; +} + +#endif // OHOS_SECURITY_H diff --git a/services/distributeddataservice/adapter/security/src/security_adapter.cpp b/services/distributeddataservice/adapter/security/src/security_adapter.cpp new file mode 100755 index 000000000..96d9f7146 --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/security_adapter.cpp @@ -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. + */ + +#include "security_adapter.h" +#include "log_print.h" +#include "security.h" +#include "1.0/dev_slinfo_mgr.h" +#undef LOG_TAG +#define LOG_TAG "SecurityAdapter" + +namespace OHOS::DistributedKv { +namespace { +class InstallDevsl { +public: + InstallDevsl(); + ~InstallDevsl(); + void Initialize(); +private: + std::shared_ptr security_ = nullptr; +}; + +InstallDevsl::InstallDevsl() +{ + (void)DEVSL_OnStart(0); + security_ = std::make_shared("distributeddata", "default", "/data/misc_de/0/mdds/Meta"); + if (security_ == nullptr) { + ZLOGD("Security is nullptr."); + return; + } + + auto status = DistributedDB::KvStoreDelegateManager::SetProcessSystemAPIAdapter(security_); + ZLOGD("set distributed db system api adapter: %d.", static_cast(status)); + + security_->InitKvStore(); +} + +InstallDevsl::~InstallDevsl() +{ + DEVSL_ToFinish(); +} + +void InstallDevsl::Initialize() +{ + if (security_ == nullptr) { + ZLOGD("Security is nullptr."); + return; + } + + security_->InitLocalCertData(); +} +__attribute__((used)) InstallDevsl g_installDevsl; +} + +KVSTORE_API void InitSecurityAdapter() +{ + if (!Security::IsFirstInit()) { + ZLOGD("Security is already inited."); + return; + } + + g_installDevsl.Initialize(); + ZLOGD("Security init finished!"); +} +} diff --git a/services/distributeddataservice/adapter/security/src/sensitive.cpp b/services/distributeddataservice/adapter/security/src/sensitive.cpp new file mode 100755 index 000000000..c0e408768 --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/sensitive.cpp @@ -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 "sensitive.h" +#include +#include "iprocess_system_api_adapter.h" +#include "log_print.h" +#include "serializable.h" +#include "1.0/dev_slinfo_mgr.h" +#undef LOG_TAG +#define LOG_TAG "Sensitive" + +namespace OHOS::DistributedKv { +Sensitive::Sensitive(std::string deviceId, uint32_t type) + : deviceId(std::move(deviceId)), securityLevel(DATA_SEC_LEVEL1), deviceType(type) +{ +} + +Sensitive::Sensitive(const std::vector &value) + : securityLevel(DATA_SEC_LEVEL1), deviceType(0) +{ + Unmarshal(value); +} + +std::vector Sensitive::Marshal() const +{ + Json::Value root; + root[GET_NAME(securityLevel)] = securityLevel; + root[GET_NAME(deviceId)] = deviceId; + root[GET_NAME(dataBase64)] = dataBase64; + root[GET_NAME(deviceType)] = deviceType; + + Json::FastWriter writer; + auto jsonStr = writer.write(root); + ZLOGD("len:%d, value:%.20s!", int32_t(jsonStr.size()), jsonStr.c_str()); + return {jsonStr.begin(), jsonStr.end()}; +} + +void Sensitive::Unmarshal(const std::vector &value) +{ + std::string input(reinterpret_cast(value.data()), value.size()); + Json::Reader reader; + Json::Value root; + ZLOGD("len:%d, value:%.20s!", int32_t(value.size()), input.c_str()); + bool success = reader.parse(input, root); + if (!success) { + ZLOGE("reader.parse failed!"); + } + + securityLevel = Serializable::GetVal(root[GET_NAME(securityLevel)], securityLevel); + deviceId = Serializable::GetVal(root[GET_NAME(deviceId)], deviceId); + dataBase64 = Serializable::GetVal(root[GET_NAME(dataBase64)], dataBase64); + deviceType = Serializable::GetVal(root[GET_NAME(deviceType)], deviceType); +} + +uint32_t Sensitive::GetSensitiveLevel() +{ + DEVSLQueryParams query; + DEVSL_INIT_PARAMS(&query); + query.udid = reinterpret_cast(deviceId.c_str()); + query.sensitiveData = reinterpret_cast(dataBase64.c_str()); + query.idLen = uint32_t(deviceId.size()); + query.sensitiveDataLen = uint32_t(dataBase64.size()); + if (dataBase64.empty()) { + query.devType = GetDevslDeviceType(); + } + + uint32_t level = DATA_SEC_LEVEL2; + uint32_t result = DEVSL_GetHighestSecLevel(&query, &level); + if (result != DEVSL_SUCCESS) { + ZLOGE("get highest level failed(%.10s)! level: %d, error: %d, cert (%.10s)", + deviceId.c_str(), securityLevel, result, dataBase64.c_str()); + return securityLevel; + } + securityLevel = level; + ZLOGD("get highest level success(%.10s)! level: %d cert (%.10s)", + deviceId.c_str(), securityLevel, dataBase64.c_str()); + return securityLevel; +} + +bool Sensitive::operator >= (const DistributedDB::SecurityOption &option) +{ + return (option.securityLabel == DistributedDB::NOT_SET) || + (GetSensitiveLevel() >= static_cast(option.securityLabel - 1)); +} + +bool Sensitive::LoadData() +{ + uint8_t data[Sensitive::MAX_DATA_LEN + 1]; + uint32_t length = Sensitive::MAX_DATA_LEN; + int32_t result = DEVSL_GetLocalCertData(data, Sensitive::MAX_DATA_LEN, &length); + if (result != DEVSL_SUCCESS) { + ZLOGE("DEVSL_GetLocalCertData failed %d", result); + return false; + } + data[length] = 0; + dataBase64 = reinterpret_cast(data); + DEVSLQueryParams query; + DEVSL_INIT_PARAMS(&query); + query.udid = reinterpret_cast(deviceId.c_str()); + query.sensitiveData = data; + query.idLen = uint32_t(deviceId.size()); + query.sensitiveDataLen = length; + + if (DEVSL_GetHighestSecLevel(&query, &securityLevel) != DEVSL_SUCCESS) { + securityLevel = DATA_SEC_LEVEL1; + } + return true; +} + +Sensitive::Sensitive(Sensitive &&sensitive) noexcept +{ + this->operator=(std::move(sensitive)); +} + +Sensitive &Sensitive::operator=(Sensitive &&sensitive) noexcept +{ + if (this == &sensitive) { + return *this; + } + deviceId = std::move(sensitive.deviceId); + dataBase64 = std::move(sensitive.dataBase64); + securityLevel = sensitive.securityLevel; + deviceType = sensitive.deviceType; + return *this; +} + +Sensitive::Sensitive(const Sensitive &sensitive) +{ + this->operator=(sensitive); +} + +Sensitive &Sensitive::operator=(const Sensitive &sensitive) +{ + if (this == &sensitive) { + return *this; + } + deviceId = sensitive.deviceId; + dataBase64 = sensitive.dataBase64; + securityLevel = sensitive.securityLevel; + deviceType = sensitive.deviceType; + return *this; +} + +uint32_t Sensitive::GetDevslDeviceType() const +{ + return deviceType; +} +} diff --git a/services/distributeddataservice/adapter/security/src/sensitive.h b/services/distributeddataservice/adapter/security/src/sensitive.h new file mode 100755 index 000000000..2aba82b70 --- /dev/null +++ b/services/distributeddataservice/adapter/security/src/sensitive.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 OHOS_SENSITIVE_H +#define OHOS_SENSITIVE_H + +#include +#include +#include + +namespace OHOS::DistributedKv { +class Sensitive final { +public: + static constexpr uint32_t MAX_DATA_LEN = 16 * 1024; + Sensitive(std::string deviceId, uint32_t type); + explicit Sensitive(const std::vector &value = {}); + Sensitive(const Sensitive &sensitive); + Sensitive &operator=(const Sensitive &sensitive); + Sensitive(Sensitive &&sensitive) noexcept; + Sensitive &operator=(Sensitive &&sensitive) noexcept; + ~Sensitive() = default; + + std::vector Marshal() const; + void Unmarshal(const std::vector &value); + + bool operator >= (const DistributedDB::SecurityOption &option); + + bool LoadData(); +private: + uint32_t GetSensitiveLevel(); + uint32_t GetDevslDeviceType() const; + + std::string deviceId {}; + std::string dataBase64 {}; + uint32_t securityLevel = 0; + uint32_t deviceType = 0; +}; +} + +#endif // OHOS_SENSITIVE_H diff --git a/services/distributeddataservice/adapter/test/BUILD.gn b/services/distributeddataservice/adapter/test/BUILD.gn new file mode 100755 index 000000000..631b8bab2 --- /dev/null +++ b/services/distributeddataservice/adapter/test/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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") +import("//build/ohos_var.gni") + +group("unittest") { + testonly = true + deps = [] + + deps += [ + "../account/test:unittest", + "../autils/test:unittest", + "../communicator/test:unittest", + "../permission/test:unittest", + "../dfx/test:unittest", + ] +} diff --git a/services/distributeddataservice/adapter/utils/BUILD.gn b/services/distributeddataservice/adapter/utils/BUILD.gn new file mode 100755 index 000000000..be2ca4773 --- /dev/null +++ b/services/distributeddataservice/adapter/utils/BUILD.gn @@ -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. +import("//build/ohos.gni") + +ohos_static_library("distributeddata_utils_static") { + sources = [ + "src/crypto_utils.cpp", + "src/kvstore_utils.cpp", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + if (build_public_version) { + cflags_cc += [ "-DCONFIG_PUBLIC_VERSION" ] + } + + remove_configs = [ "//build/config/compiler:no_exceptions" ] + + configs = [ "//build/config/compiler:exceptions" ] + + include_dirs = [ + "../include/permission", + "../include/utils", + "../include/log", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//utils/native/base/include", + "../include/dfx", + "//third_party/openssl/include/", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + + external_deps = [ + "aafwk_standard:base", + "aafwk_standard:intent", + "appexecfwk_standard:appexecfwk_base", + "appexecfwk_standard:appexecfwk_core", + "bytrace_standard:bytrace_core", + "bytrace_standard:bytrace_core", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + ] +} diff --git a/services/distributeddataservice/adapter/utils/src/crypto_utils.cpp b/services/distributeddataservice/adapter/utils/src/crypto_utils.cpp new file mode 100755 index 000000000..86f1831e3 --- /dev/null +++ b/services/distributeddataservice/adapter/utils/src/crypto_utils.cpp @@ -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. + */ + +#include "crypto_utils.h" + +#include +#include +#include +#include +#include "openssl/sha.h" + +namespace OHOS { +namespace DistributedKv { +std::string CryptoUtils::Sha256(const std::string &plainText) +{ + unsigned char hash[SHA256_DIGEST_LENGTH] = ""; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, plainText.c_str(), plainText.size()); + SHA256_Final(hash, &ctx); + std::string hashToHex; + // here we translate sha256 hash to hexadecimal. each 8-bit char will be presented by two characters([0-9a-f]) + constexpr int CHAR_WIDTH = 8; + constexpr int HEX_WIDTH = 4; + constexpr unsigned char HEX_MASK = 0xf; + constexpr int HEX_A = 10; + hashToHex.reserve(SHA256_DIGEST_LENGTH * (CHAR_WIDTH / HEX_WIDTH)); + for (unsigned char i : hash) { + unsigned char hex = i >> HEX_WIDTH; + if (hex < HEX_A) { + hashToHex.push_back('0' + hex); + } else { + hashToHex.push_back('a' + hex - HEX_A); + } + hex = i & HEX_MASK; + if (hex < HEX_A) { + hashToHex.push_back('0' + hex); + } else { + hashToHex.push_back('a' + hex - HEX_A); + } + } + return hashToHex; +} + +std::string CryptoUtils::Sha256AppId(const std::string &bundleName, int userId) +{ + return bundleName; +} + +std::string CryptoUtils::Sha256UserId(const std::string &plainText) +{ + std::regex pattern("^[0-9]+$"); + if (!std::regex_match(plainText, pattern)) { + return plainText; + } + + std::string::size_type sizeType; + int64_t plainVal; + std::string::size_type int64MaxLen(std::to_string(INT64_MAX).size()); + // plain text length must be less than INT64_MAX string. + try { + plainVal = static_cast(std::stoll(plainText, &sizeType)); + } catch (const std::out_of_range &) { + plainVal = static_cast(std::stoll( + plainText.substr(plainText.size() - int64MaxLen + 1, int64MaxLen - 1), &sizeType)); + } catch (const std::exception &) { + return plainText; + } + + union UnionLong { + int64_t val; + unsigned char byteLen[sizeof(int64_t)]; + }; + UnionLong unionLong {}; + unionLong.val = plainVal; + std::list unionList(std::begin(unionLong.byteLen), std::end(unionLong.byteLen)); + unionList.reverse(); + std::vector unionVec(unionList.begin(), unionList.end()); + + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, unionVec.data(), sizeof(int64_t)); + SHA256_Final(hash, &ctx); + + const char* hexArray = "0123456789ABCDEF"; + char* hexChars = new char[SHA256_DIGEST_LENGTH * 2 + 1]; + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + unsigned int value = hash[i] & 0xFF; + hexChars[i * 2] = hexArray[value >> 4]; + hexChars[i * 2 + 1] = hexArray[value & 0x0F]; + } + hexChars[SHA256_DIGEST_LENGTH * 2] = '\0'; + std::string res(hexChars); + delete []hexChars; + return res; +} + +void CryptoUtils::GetRandomKey(int keyLen, std::vector &key) +{ + constexpr int UINT8_T_MAX = 255; + std::random_device randomDevice; + std::uniform_int_distribution distribution(0, UINT8_T_MAX); + key.clear(); + for (int i = 0; i < keyLen; i++) { + key.push_back(static_cast(distribution(randomDevice))); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/adapter/utils/src/kvstore_utils.cpp b/services/distributeddataservice/adapter/utils/src/kvstore_utils.cpp new file mode 100755 index 000000000..aa495ba1e --- /dev/null +++ b/services/distributeddataservice/adapter/utils/src/kvstore_utils.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. + */ + +#define LOG_TAG "KvStoreUtils" + +#include "kvstore_utils.h" +#include "crypto_utils.h" +#include "ipc_skeleton.h" +#include "log_print.h" +#include "permission_validator.h" + +namespace OHOS { +namespace DistributedKv { +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 = "******"; +std::string KvStoreUtils::GetAppIdByBundleName(const std::string &bundleName) +{ + return bundleName; +} + +std::string KvStoreUtils::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)); +} + +AppDistributedKv::CommunicationProvider &KvStoreUtils::GetProviderInstance() +{ +#ifdef CONFIG_PUBLIC_VERSION + return AppDistributedKv::CommunicationProvider::GetInstance(); +#else + return *(AppDistributedKv::CommunicationProvider::MakeCommunicationProvider().get()); +#endif +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/BUILD.gn b/services/distributeddataservice/app/BUILD.gn new file mode 100755 index 000000000..38b166f4c --- /dev/null +++ b/services/distributeddataservice/app/BUILD.gn @@ -0,0 +1,118 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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") +import("//build/ohos_var.gni") + +group("build_module") { + deps = [ ":distributeddataservice" ] + if (build_public_version) { + deps += [ + ":distributed_data.rc", + ":distributeddata_profile", + ] + } +} + +ohos_prebuilt_etc("distributed_data.rc") { + source = "distributed_data.rc" + relative_install_dir = "init" + subsystem_name = "distributeddatamgr" +} + +ohos_sa_profile("distributeddata_profile") { + sources = [ "../sa_profile/1301.xml" ] + + subsystem_name = "distributeddatamgr" +} + +config("module_private_config") { + visibility = [ ":*" ] + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//utils/system/safwk/native/include", + "../adapter/include/account", + "../adapter/include/permission", + "../adapter/include/uninstaller", + "../adapter/include/broadcaster", + "../adapter/include/utils", + "../adapter/include/security", + "../adapter/include", + + # for ipc_core interfaces. + "//utils/native/base/include", + "include", + "src", + "//third_party/json/single_include", + ] + + cflags = [ "-Wno-multichar" ] + + cflags_cc = [ "-fvisibility=hidden" ] +} + +ohos_shared_library("distributeddataservice") { + sources = [ + "src/backup_handler.cpp", + "src/device_change_listener_impl.cpp", + "src/device_kvstore_impl.cpp", + "src/device_kvstore_observer_impl.cpp", + "src/device_kvstore_resultset_impl.cpp", + "src/kvstore_account_observer.cpp", + "src/kvstore_app_accessor.cpp", + "src/kvstore_app_manager.cpp", + "src/kvstore_data_service.cpp", + "src/kvstore_impl.cpp", + "src/kvstore_meta_manager.cpp", + "src/kvstore_observer_impl.cpp", + "src/kvstore_resultset_impl.cpp", + "src/kvstore_snapshot_impl.cpp", + "src/kvstore_sync_manager.cpp", + "src/kvstore_user_manager.cpp", + "src/query_helper.cpp", + "src/single_kvstore_impl.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//utils/native/base:utils", + ] + + external_deps = [ + "appexecfwk_standard:appexecfwk_base", + "appexecfwk_standard:appexecfwk_core", + + # "battery_manager:batterysrv_client", + "aafwk_standard:base", + "aafwk_standard:intent", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + + # "power_manager:powermgr_client", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + "startup_l2:syspara", + ] + + subsystem_name = "distributeddatamgr" +} diff --git a/services/distributeddataservice/app/distributed_data.rc b/services/distributeddataservice/app/distributed_data.rc new file mode 100644 index 000000000..7ea8455d1 --- /dev/null +++ b/services/distributeddataservice/app/distributed_data.rc @@ -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. + +on boot + start distributeddata + +service distributeddata /system/bin/sa_main /system/profile/distributeddata.xml + class z_core + capabilities DAC_READ_SEARCH + user system + group system shell readproc + seclabel u:r:distributeddata:s0 + writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks /dev/blkio/foreground/tasks diff --git a/services/distributeddataservice/app/src/backup_handler.cpp b/services/distributeddataservice/app/src/backup_handler.cpp new file mode 100755 index 000000000..e10aeb245 --- /dev/null +++ b/services/distributeddataservice/app/src/backup_handler.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BackupHandler" + +#include "backup_handler.h" +#include +#include +#include +#include +#include "account_delegate.h" +#include "constant.h" +#include "crypto_utils.h" +#include "kv_store_delegate_manager.h" +#include "kv_scheduler.h" +#include "kvstore_data_service.h" +#include "log_print.h" +#include "kvstore_meta_manager.h" + +namespace OHOS::DistributedKv { +using json = nlohmann::json; + +BackupHandler::BackupHandler(IKvStoreDataService *kvStoreDataService) +{ +} + +BackupHandler::BackupHandler() +{ +} +BackupHandler::~BackupHandler() +{ +} +void BackupHandler::BackSchedule() +{ + std::chrono::duration delay(1800); // delay 30 minutes + std::chrono::duration internal(1800); // duration is 30 minutes + ZLOGI("BackupHandler Schedule start."); + scheduler_.Every(delay, internal, [&]() { + if (!CheckNeedBackup()) { + ZLOGE("it is not meet the condition of backup."); + return; + } + std::map results; + ZLOGI("BackupHandler Schedule Every start."); + if (KvStoreMetaManager::GetInstance().GetFullMetaData(results)) { + ZLOGE("GetFullMetaData failed."); + return; + } + + for (auto const &entry : results) { + if (!entry.second.kvStoreMetaData.isBackup || entry.second.kvStoreMetaData.isDirty) { + continue; + } + + KvStoreType type = entry.second.kvStoreMetaData.kvStoreType; + if (type == KvStoreType::MULTI_VERSION) { + MultiKvStoreBackup(entry.second); + } else if (type == KvStoreType::SINGLE_VERSION) { + SingleKvStoreBackup(entry.second); + } + } + }); +} + +void BackupHandler::SingleKvStoreBackup(const MetaData &metaData) +{ + ZLOGI("SingleKvStoreBackup start."); + auto pathType = KvStoreAppManager::ConvertPathType( + metaData.kvStoreMetaData.bundleName, metaData.kvStoreMetaData.securityLevel); + if (!ForceCreateDirectory(BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType))) { + ZLOGE("SingleKvStoreBackup backup create directory failed."); + return; + } + + DistributedDB::CipherPassword password; + const std::vector &secretKey = metaData.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Set secret key failed."); + return; + } + + DistributedDB::KvStoreNbDelegate::Option dbOption; + dbOption.createIfNecessary = false; + dbOption.isEncryptedDb = password.GetSize() > 0; + dbOption.passwd = password; + dbOption.createDirByStoreIdOnly = true; + dbOption.secOption = KvStoreAppManager::ConvertSecurity(metaData.kvStoreMetaData.securityLevel); + + auto *delegateMgr = new DistributedDB::KvStoreDelegateManager(metaData.kvStoreMetaData.appId, + AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(metaData.kvStoreMetaData.bundleName)); + + std::string appDataStoragePath = KvStoreAppManager::GetDataStoragePath(metaData.kvStoreMetaData.deviceAccountId, + metaData.kvStoreMetaData.bundleName, pathType); + delegateMgr->SetKvStoreConfig( + { Constant::Concatenate({appDataStoragePath, "/", metaData.kvStoreMetaData.bundleName })}); + + std::function fun = + [&](DistributedDB::DBStatus status, DistributedDB::KvStoreNbDelegate *delegate) { + auto del = std::shared_ptr(delegateMgr); + if (delegate == nullptr) { + ZLOGE("SingleKvStoreBackup delegate is null"); + return; + } + if (metaData.kvStoreMetaData.isAutoSync) { + bool autoSync = true; + DistributedDB::PragmaData data = static_cast(&autoSync); + auto pragmaStatus = delegate->Pragma(DistributedDB::PragmaCmd::AUTO_SYNC, data); + if (pragmaStatus != DistributedDB::DBStatus::OK) { + ZLOGE("pragmaStatus: %d", static_cast(pragmaStatus)); + } + } + + ZLOGW("SingleKvStoreBackup export"); + if (status == DistributedDB::DBStatus::OK) { + std::string backupName = Constant::Concatenate( + { metaData.kvStoreMetaData.userId, "_", metaData.kvStoreMetaData.appId, "_", + metaData.kvStoreMetaData.storeId }); + auto backupFullName = Constant::Concatenate({ + BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType), "/", + GetHashedBackupName(backupName) + }); + auto backupBackFullName = Constant::Concatenate({ backupFullName, ".", "backup" }); + RenameFile(backupFullName, backupBackFullName); + status = delegate->Export(backupFullName, dbOption.passwd); + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("SingleKvStoreBackup export success."); + RemoveFile(backupBackFullName); + } else { + ZLOGE("SingleKvStoreBackup export failed, status is %d.", status); + RenameFile(backupBackFullName, backupFullName); + } + } + del->CloseKvStore(delegate); + }; + delegateMgr->GetKvStore(metaData.kvStoreMetaData.storeId, dbOption, fun); +} + +void BackupHandler::MultiKvStoreBackup(const MetaData &metaData) +{ + auto pathType = KvStoreAppManager::ConvertPathType(metaData.kvStoreMetaData.bundleName, + metaData.kvStoreMetaData.securityLevel); + if (!ForceCreateDirectory(BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType))) { + ZLOGE("MultiKvStoreBackup backup create directory failed."); + return; + } + ZLOGI("MultiKvStoreBackup start."); + + DistributedDB::CipherPassword password; + const std::vector &secretKey = metaData.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Set secret key value failed. len is (%d)", int32_t(secretKey.size())); + return; + } + + DistributedDB::KvStoreDelegate::Option option; + option.createIfNecessary = false; + option.isEncryptedDb = password.GetSize() > 0; + option.passwd = password; + option.createDirByStoreIdOnly = true; + + auto *delegateMgr = new DistributedDB::KvStoreDelegateManager(metaData.kvStoreMetaData.appId, + AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(metaData.kvStoreMetaData.bundleName)); + std::string appDataStoragePath = KvStoreAppManager::GetDataStoragePath(metaData.kvStoreMetaData.deviceAccountId, + metaData.kvStoreMetaData.bundleName, pathType); + delegateMgr->SetKvStoreConfig( + {Constant::Concatenate({appDataStoragePath, "/", metaData.kvStoreMetaData.bundleName})}); + std::function fun = + [&](DistributedDB::DBStatus status, DistributedDB::KvStoreDelegate *delegate) { + auto del = std::shared_ptr(delegateMgr); + if (delegate == nullptr) { + ZLOGE("MultiKvStoreBackup delegate is null"); + return; + } + ZLOGW("MultiKvStoreBackup export"); + if (status == DistributedDB::DBStatus::OK) { + std::string backupName = + Constant::Concatenate({metaData.kvStoreMetaData.userId, "_", + metaData.kvStoreMetaData.appId, "_", metaData.kvStoreMetaData.storeId}); + auto backupFullName = Constant::Concatenate({ + BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType), "/", + GetHashedBackupName(backupName) + }); + auto backupBackFullName = Constant::Concatenate({backupFullName, ".", "backup"}); + RenameFile(backupFullName, backupBackFullName); + status = delegate->Export(backupFullName, option.passwd); + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("MultiKvStoreBackup export success."); + RemoveFile(backupBackFullName); + ZLOGD("MultiKvStoreBackup export success."); + } else { + ZLOGE("MultiKvStoreBackup export failed."); + RenameFile(backupBackFullName, backupFullName); + } + } + del->CloseKvStore(delegate); + }; + delegateMgr->GetKvStore(metaData.kvStoreMetaData.storeId, option, fun); +} + +bool BackupHandler::SingleKvStoreRecover(MetaData &metaData, DistributedDB::KvStoreNbDelegate *delegate) +{ + ZLOGI("start."); + if (delegate == nullptr) { + ZLOGE("SingleKvStoreRecover failed, delegate is null."); + return false; + } + auto pathType = KvStoreAppManager::ConvertPathType(metaData.kvStoreMetaData.bundleName, + metaData.kvStoreMetaData.securityLevel); + if (!BackupHandler::FileExists(BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType))) { + ZLOGE("SingleKvStoreRecover failed, backupDir_ file is not exist."); + return false; + } + + DistributedDB::CipherPassword password; + const std::vector &secretKey = metaData.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Set secret key failed."); + return false; + } + + std::string backupName = Constant::Concatenate( + {metaData.kvStoreMetaData.userId, "_", metaData.kvStoreMetaData.appId, "_", + metaData.kvStoreMetaData.storeId}); + auto backupFullName = Constant::Concatenate({ + BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType), "/", + GetHashedBackupName(backupName) + }); + DistributedDB::DBStatus dbStatus = delegate->Import(backupFullName, password); + if (dbStatus == DistributedDB::DBStatus::OK) { + ZLOGI("SingleKvStoreRecover success."); + return true; + } + ZLOGI("SingleKvStoreRecover failed."); + return false; +} + +bool BackupHandler::MultiKvStoreRecover(MetaData &metaData, + DistributedDB::KvStoreDelegate *delegate) +{ + ZLOGI("start."); + if (delegate == nullptr) { + ZLOGE("MultiKvStoreRecover failed, delegate is null."); + return false; + } + auto pathType = KvStoreAppManager::ConvertPathType(metaData.kvStoreMetaData.bundleName, + metaData.kvStoreMetaData.securityLevel); + if (!BackupHandler::FileExists(BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType))) { + ZLOGE("MultiKvStoreRecover failed, backupDir_ file is not exist."); + return false; + } + + ZLOGI("MultiKvStoreRecover start."); + DistributedDB::CipherPassword password; + const std::vector &secretKey = metaData.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Set secret key failed."); + return false; + } + + std::string backupName = Constant::Concatenate( + {metaData.kvStoreMetaData.userId, "_", metaData.kvStoreMetaData.appId, "_", + metaData.kvStoreMetaData.storeId}); + auto backupFullName = Constant::Concatenate({ + BackupHandler::GetBackupPath(metaData.kvStoreMetaData.deviceAccountId, pathType), "/", + GetHashedBackupName(backupName) + }); + DistributedDB::DBStatus dbStatus = delegate->Import(backupFullName, password); + if (dbStatus == DistributedDB::DBStatus::OK) { + ZLOGI("MultiKvStoreRecover success."); + return true; + } + ZLOGI("MultiKvStoreRecover failed."); + return false; +} + +std::string BackupHandler::backupDirCe_; +std::string BackupHandler::backupDirDe_; +const std::string &BackupHandler::GetBackupPath(const std::string &deviceAccountId, int type) +{ + if (type == KvStoreAppManager::PATH_DE) { + if (backupDirDe_.empty()) { + backupDirDe_ = Constant::Concatenate({ Constant::ROOT_PATH_DE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), + "/", "backup" }); + } + return backupDirDe_; + } else { + if (backupDirCe_.empty()) { + backupDirCe_ = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), + "/", "backup" }); + } + return backupDirCe_; + } +} + +bool BackupHandler::RenameFile(const std::string &oldPath, const std::string &newPath) +{ + if (oldPath.empty() || newPath.empty()) { + ZLOGE("RenameFile failed: path is empty"); + return false; + } + if (!RemoveFile(newPath)) { + ZLOGE("RenameFile failed: newPath file is already exist"); + return false; + } + if (rename(oldPath.c_str(), newPath.c_str()) != 0) { + ZLOGE("RenameFile: rename error, errno[%d].", errno); + return false; + } + return true; +} + +bool BackupHandler::RemoveFile(const std::string &path) +{ + if (path.empty()) { + ZLOGI("RemoveFile: path is empty"); + return true; + } + + if (unlink(path.c_str()) != 0 && (errno != ENOENT)) { + ZLOGE("RemoveFile: failed to RemoveFile, errno[%d].", errno); + return false; + } + return true; +} + +bool BackupHandler::FileExists(const std::string &path) +{ + if (path.empty()) { + ZLOGI("FileExists: path is empty"); + return false; + } + + if (access(path.c_str(), F_OK) != 0) { + ZLOGI("FileExists: file is not exist"); + return false; + } + return true; +} + +bool BackupHandler::CheckNeedBackup() +{ + return false; +} + +std::string BackupHandler::GetHashedBackupName(const std::string &bundleName) +{ + if (bundleName.empty()) { + return bundleName; + } + return CryptoUtils::Sha256(bundleName); +} +} // namespace OHOS::DistributedKv diff --git a/services/distributeddataservice/app/src/backup_handler.h b/services/distributeddataservice/app/src/backup_handler.h new file mode 100755 index 000000000..408dbe99b --- /dev/null +++ b/services/distributeddataservice/app/src/backup_handler.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 DISTRIBUTEDDATAMGR_BACKUP_HANDLER_H +#define DISTRIBUTEDDATAMGR_BACKUP_HANDLER_H + +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate.h" +#include "types.h" +#include "kv_scheduler.h" +#include "kvstore_meta_manager.h" +#include "ikvstore_data_service.h" + +namespace OHOS::DistributedKv { +class BackupHandler { +public: + explicit BackupHandler(IKvStoreDataService *kvStoreDataService); + BackupHandler(); + ~BackupHandler(); + void BackSchedule(); + void SingleKvStoreBackup(const MetaData &metaData); + void MultiKvStoreBackup(const MetaData &metaData); + bool SingleKvStoreRecover(MetaData &metaData, DistributedDB::KvStoreNbDelegate *delegate); + bool MultiKvStoreRecover(MetaData &metaData, DistributedDB::KvStoreDelegate *delegate); + + static const std::string &GetBackupPath(const std::string &deviceAccountId, int pathType); + static bool RenameFile(const std::string &oldPath, const std::string &newPath); + static bool RemoveFile(const std::string &path); + static bool FileExists(const std::string &path); + static std::string GetHashedBackupName(const std::string &bundleName); +private: + KvScheduler scheduler_ {}; + static std::string backupDirCe_; + static std::string backupDirDe_; + bool CheckNeedBackup(); +}; +} // namespace OHOS::DistributedKv +#endif // DISTRIBUTEDDATAMGR_BACKUP_HANDLER_H diff --git a/services/distributeddataservice/app/src/device_change_listener_impl.cpp b/services/distributeddataservice/app/src/device_change_listener_impl.cpp new file mode 100644 index 000000000..5c2034416 --- /dev/null +++ b/services/distributeddataservice/app/src/device_change_listener_impl.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DeviceKvStoreImpl" + +#include "device_change_listener_impl.h" +#include "kvstore_utils.h" +#include "log_print.h" + +using namespace OHOS::AppDistributedKv; +namespace OHOS::DistributedKv { +DeviceChangeListenerImpl::DeviceChangeListenerImpl(std::map> &observers) : observers_(observers) +{} + +void DeviceChangeListenerImpl::OnDeviceChanged(const AppDistributedKv::DeviceInfo &info, + const AppDistributedKv::DeviceChangeType &type) const +{ + DeviceChangeType deviceType = type == AppDistributedKv::DeviceChangeType::DEVICE_ONLINE ? + DeviceChangeType::DEVICE_ONLINE : DeviceChangeType::DEVICE_OFFLINE; + auto nodeid = KvStoreUtils::GetProviderInstance().ToNodeId(info.deviceId); + ZLOGD("networkid:%s", nodeid.c_str()); + ZLOGD("uuid:%s", KvStoreUtils::ToBeAnonymous(info.deviceId).c_str()); + for (auto const &observer : observers_) { + observer.second->OnChange({nodeid, info.deviceName, info.deviceType}, deviceType); + } +} +AppDistributedKv::ChangeLevelType DeviceChangeListenerImpl::GetChangeLevelType() const +{ + return AppDistributedKv::ChangeLevelType::MIN; +} +} diff --git a/services/distributeddataservice/app/src/device_change_listener_impl.h b/services/distributeddataservice/app/src/device_change_listener_impl.h new file mode 100755 index 000000000..0e56b6dca --- /dev/null +++ b/services/distributeddataservice/app/src/device_change_listener_impl.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 DEV_DEVICE_CHANGE_LISTENER_IMPL_H +#define DEV_DEVICE_CHANGE_LISTENER_IMPL_H + +#include +#include "app_device_status_change_listener.h" +#include "idevice_status_change_listener.h" + +namespace OHOS::DistributedKv { +class DeviceChangeListenerImpl : public AppDistributedKv::AppDeviceStatusChangeListener { +public: + explicit DeviceChangeListenerImpl(std::map> &observers); + void OnDeviceChanged(const AppDistributedKv::DeviceInfo &info, + const AppDistributedKv::DeviceChangeType &type) const override; + AppDistributedKv::ChangeLevelType GetChangeLevelType() const override; + ~DeviceChangeListenerImpl() {} +private: + std::map> &observers_; +}; +} +#endif // DEV_DEVICE_CHANGE_LISTENER_IMPL_H diff --git a/services/distributeddataservice/app/src/device_kvstore_impl.cpp b/services/distributeddataservice/app/src/device_kvstore_impl.cpp new file mode 100644 index 000000000..2594b6b90 --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_impl.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DeviceKvStoreImpl" + +#include "device_kvstore_impl.h" +#include +#include "kvstore_utils.h" +#include "constant.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +using namespace AppDistributedKv; + +DeviceKvStoreImpl::DeviceKvStoreImpl(const KvStoreParams ¶ms, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) + : SingleKvStoreImpl(params.options, params.deviceAccountId, params.appId, params.storeId, + params.appDirectory, kvStoreNbDelegate), params_(params) +{} + +DeviceKvStoreImpl::~DeviceKvStoreImpl() +{} + +Status DeviceKvStoreImpl::Get(const Key &key, Value &value) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::Get(key, value); + } + + std::vector tmpkey; + Status status = DeleteKeyPrefix(key, tmpkey); + if (status != Status::SUCCESS) { + ZLOGE("get deviceid failed."); + return status; + } + KeyEncap ke {static_cast(localDeviceId_.length())}; + tmpkey.insert(tmpkey.end(), std::begin(ke.byteLen), std::end(ke.byteLen)); + Key decorateKey(tmpkey); + return SingleKvStoreImpl::Get(decorateKey, value); +} + +Status DeviceKvStoreImpl::Put(const Key &key, const Value &value) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::Put(key, value); + } + + std::vector tmpkey; + AddKeyPrefixAndSuffix(key, tmpkey); + Key decorateKey(tmpkey); + return SingleKvStoreImpl::Put(decorateKey, value); +} + +Status DeviceKvStoreImpl::Delete(const Key &key) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::Delete(key); + } + + std::vector tmpkey; + AddKeyPrefixAndSuffix(key, tmpkey); + Key decorateKey(tmpkey); + return SingleKvStoreImpl::Delete(decorateKey); +} + +Status DeviceKvStoreImpl::GetEntries(const Key &prefixKey, std::vector &entries) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::GetEntries(prefixKey, entries); + } + + std::vector tmpkey; + Status status = DeleteKeyPrefix(prefixKey, tmpkey); + if (status == Status::KEY_NOT_FOUND) { + ZLOGE("DeleteKeyPrefix deviceId is invalid."); + return Status::SUCCESS; + } + + if (status != Status::SUCCESS) { + ZLOGE("DeleteKeyPrefix failed."); + return Status::ERROR; + } + Key decorateKey(tmpkey); + std::vector tmpEntries; + Status ret = SingleKvStoreImpl::GetEntries(decorateKey, tmpEntries); + if (ret != Status::SUCCESS) { + ZLOGE("GetEntries status=%d", static_cast(ret)); + return ret; + } + + for (const auto &entry : tmpEntries) { + std::vector tmpkey; + DeletePrefixAndSuffix(entry.key, tmpkey); + Key retKey(tmpkey); + Entry en; + en.key = retKey; + en.value = entry.value; + entries.push_back(en); + } + return ret; +} + +Status DeviceKvStoreImpl::RemoveDeviceData(const std::string &device) +{ + return SingleKvStoreImpl::RemoveDeviceData(device); +} + +void DeviceKvStoreImpl::GetResultSet(const Key &prefixKey, + std::function)> callback) +{ + if (!params_.deviceCoordinate) { + SingleKvStoreImpl::GetResultSet(prefixKey, callback); + return; + } + + std::vector tmpkey; + Status status = DeleteKeyPrefix(prefixKey, tmpkey); + if (status != Status::SUCCESS) { + ZLOGE("DeleteKeyPrefix failed."); + callback(status, nullptr); + return; + } + Key decorateKey(tmpkey); + SingleKvStoreImpl::GetResultSet(decorateKey, callback, true); +} + +Status DeviceKvStoreImpl::SubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::SubscribeKvStore(subscribeType, observer); + } + + return SingleKvStoreImpl::SubscribeKvStore(subscribeType, observer, true); +} + +Status DeviceKvStoreImpl::PutBatch(const std::vector &entries) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::PutBatch(entries); + } + + std::vector tmpEntries = entries; + for (auto &entry : tmpEntries) { + std::vector tmpkey; + AddKeyPrefixAndSuffix(entry.key, tmpkey); + Key decorateKey(tmpkey); + entry.key = decorateKey; + } + + return SingleKvStoreImpl::PutBatch(tmpEntries); +} + +Status DeviceKvStoreImpl::DeleteBatch(const std::vector &keys) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::DeleteBatch(keys); + } + + std::vector tmpKeys = keys; + for (auto &key : tmpKeys) { + std::vector tmpkey; + AddKeyPrefixAndSuffix(key, tmpkey); + Key decorateKey(tmpkey); + key = decorateKey; + } + return SingleKvStoreImpl::DeleteBatch(tmpKeys); +} + +Status DeviceKvStoreImpl::GetEntriesWithQuery(const std::string &query, std::vector &entries) +{ + if (!params_.deviceCoordinate) { + return SingleKvStoreImpl::GetEntriesWithQuery(query, entries); + } + + Status ret = SingleKvStoreImpl::GetEntriesWithQuery(query, entries); + if (ret != Status::SUCCESS) { + return ret; + } + + for (auto &entry : entries) { + std::vector tmpkey; + DeletePrefixAndSuffix(entry.key, tmpkey); + Key retKey(tmpkey); + entry.key = retKey; + } + return ret; +} + +void DeviceKvStoreImpl::GetResultSetWithQuery(const std::string &query, + std::function)> callback) +{ + if (!params_.deviceCoordinate) { + SingleKvStoreImpl::GetResultSetWithQuery(query, callback); + return; + } + + SingleKvStoreImpl::GetResultSetWithQuery(query, callback, true); +} + +std::string DeviceKvStoreImpl::localDeviceId_; +std::string DeviceKvStoreImpl::GetLocalDeviceId() +{ + if (!localDeviceId_.empty()) { + return localDeviceId_; + } + + localDeviceId_ = KvStoreUtils::GetProviderInstance().GetLocalDevice().deviceId; + return localDeviceId_; +} + +Status DeviceKvStoreImpl::DeleteKeyPrefix(const Key &in, std::vector &out) +{ + GetLocalDeviceId(); + // |head length|nodeid ID| original ID| + // |----4------|---------|------------| + // indicate head len is 4; this key is from java; + size_t deviceIdPrefixLen = 4; + auto inData = in.Data(); + if (inData.size() < deviceIdPrefixLen) { + ZLOGE("inData length is error."); + return Status::ERROR; + } + std::string deviceIdLenStr(inData.begin(), inData.begin() + deviceIdPrefixLen); + std::regex pattern("^[0-9]*$"); + if (!std::regex_match(deviceIdLenStr, pattern)) { + ZLOGE("device id length is error."); + return Status::ERROR; + } + std::string nodeid(inData.begin() + deviceIdPrefixLen, inData.begin() + deviceIdPrefixLen + localDeviceId_.size()); + std::string deviceUuID = KvStoreUtils::GetProviderInstance().GetUuidByNodeId(nodeid); + if (deviceUuID.empty()) { + ZLOGE("device uuid is empty."); + return Status::KEY_NOT_FOUND; + } + out.insert(out.end(), deviceUuID.begin(), deviceUuID.end()); + + std::string original (inData.begin() + deviceIdPrefixLen + localDeviceId_.size(), inData.end()); + out.insert(out.end(), original.begin(), original.end()); + return Status::SUCCESS; +} + +// need to delete prefix UDID and suffix UDID length; +void DeviceKvStoreImpl::DeletePrefixAndSuffix(const Key &in, std::vector &out) +{ + GetLocalDeviceId(); + if (localDeviceId_.empty()) { + ZLOGE("Get deviceid failed."); + return; + } + out.insert(out.end(), in.Data().begin() + localDeviceId_.size(), in.Data().end() - sizeof(int)); +} + +// need to add UDID to key prefix, add UDID length to key suffix to adapter 1.0; +// | UDID | KEY | UDID len | +bool DeviceKvStoreImpl::AddKeyPrefixAndSuffix(const Key &in, std::vector &out) +{ + GetLocalDeviceId(); + if (localDeviceId_.empty()) { + ZLOGE("Get deviceid failed."); + return false; + } + // device ID + out.insert(out.end(), localDeviceId_.begin(), localDeviceId_.end()); + // the original key + out.insert(out.end(), in.Data().begin(), in.Data().end()); + // add device ID length to the tail, which include 4 uint8_t; + KeyEncap ke {static_cast(localDeviceId_.length())}; + out.insert(out.end(), std::begin(ke.byteLen), std::end(ke.byteLen)); + return true; +} +} diff --git a/services/distributeddataservice/app/src/device_kvstore_impl.h b/services/distributeddataservice/app/src/device_kvstore_impl.h new file mode 100755 index 000000000..c118b47cf --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_impl.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 DEVICE_KVSTORE_IMPL_H +#define DEVICE_KVSTORE_IMPL_H + +#include "kvstore_common.h" +#include "single_kvstore_impl.h" + +namespace OHOS::DistributedKv { +union KeyEncap { + int len; + uint8_t byteLen[sizeof(int)]; +} __attribute__((packed)); + +class DeviceKvStoreImpl : public SingleKvStoreImpl { +public: + DeviceKvStoreImpl(const KvStoreParams ¶ms, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + ~DeviceKvStoreImpl(); + Status Put(const Key &key, const Value &value) override; + Status Delete(const Key &key) override; + Status Get(const Key &key, Value &value) override; + Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer) override; + Status GetEntries(const Key &prefixKey, std::vector &entries) override; + Status GetEntriesWithQuery(const std::string &query, std::vector &entries) override; + void GetResultSet(const Key &prefixKey, std::function)> callback) override; + void GetResultSetWithQuery(const std::string &query, + std::function)> callback) override; + Status RemoveDeviceData(const std::string &device) override; + Status PutBatch(const std::vector &entries) override; + Status DeleteBatch(const std::vector &keys) override; + static std::string GetLocalDeviceId(); +private: + bool AddKeyPrefixAndSuffix(const Key &in, std::vector &out); + Status DeleteKeyPrefix(const Key &in, std::vector &out); + void DeletePrefixAndSuffix(const Key &in, std::vector &out); + static std::string localDeviceId_; + KvStoreParams params_; +}; +} +#endif // DEVICE_KVSTORE_IMPL_H diff --git a/services/distributeddataservice/app/src/device_kvstore_observer_impl.cpp b/services/distributeddataservice/app/src/device_kvstore_observer_impl.cpp new file mode 100644 index 000000000..85305dbb4 --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_observer_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. + */ + +#define LOG_TAG "AppDistributedKvDataManagerImpl" + +#include "device_kvstore_observer_impl.h" +#include "kvstore_utils.h" +#include "log_print.h" +using namespace OHOS::AppDistributedKv; +namespace OHOS::DistributedKv { +DeviceKvStoreObserverImpl::DeviceKvStoreObserverImpl(SubscribeType subscribeType, sptr observerProxy, + bool deviceSync) : KvStoreObserverImpl(subscribeType, observerProxy), deviceSync_(deviceSync), + localDeviceId_{}, observerProxy_(observerProxy) +{} + +DeviceKvStoreObserverImpl::~DeviceKvStoreObserverImpl() +{} + +void DeviceKvStoreObserverImpl::OnChange(const DistributedDB::KvStoreChangedData &data) +{ + if (deviceSync_) { + std::list insertList = data.GetEntriesInserted(); + std::list updateList = data.GetEntriesUpdated(); + std::list deletedList = data.GetEntriesDeleted(); + + std::list insertListTmp; + std::list updateListTmp; + std::list deletedListTmp; + std::string deviceId; + Transfer(insertList, insertListTmp, deviceId); + Transfer(updateList, updateListTmp, deviceId); + Transfer(deletedList, deletedListTmp, deviceId); + if (deviceId.empty()) { + ZLOGE("Did NOT find any valid deviceId"); + } + ChangeNotification changeNotification(insertListTmp, updateListTmp, deletedListTmp, deviceId, false); + if (observerProxy_ != nullptr) { + observerProxy_->OnChange(changeNotification, nullptr); + } + return; + } else { + KvStoreObserverImpl::OnChange(data); + } +} + +void DeviceKvStoreObserverImpl::Transfer(const std::list &input, std::list &output, + std::string &deviceId) +{ + if (localDeviceId_.empty()) { + auto localDevId = KvStoreUtils::GetProviderInstance().GetLocalDevice().deviceId; + if (localDevId.empty()) { + return; + } + localDeviceId_ = localDevId; + } + + for (const auto &entry : input) { + if (localDeviceId_.size() > entry.key.size()) { + continue; + } + if (deviceId.empty()) { + ZLOGI("Get deviceId from Entry."); + auto uuid = std::string(entry.key.begin(), entry.key.begin() + localDeviceId_.length()); + if (localDeviceId_.compare(uuid) == 0) { + deviceId = KvStoreUtils::GetProviderInstance().GetLocalBasicInfo().deviceId; + } else { + deviceId = KvStoreUtils::GetProviderInstance().ToNodeId(uuid); + } + } + std::vector decorateKey(entry.key.begin() + localDeviceId_.length(), entry.key.end() - sizeof(int)); + Entry tmpEntry; + tmpEntry.key = decorateKey; + tmpEntry.value = entry.value; + output.push_back(tmpEntry); + } +} +} \ No newline at end of file diff --git a/services/distributeddataservice/app/src/device_kvstore_observer_impl.h b/services/distributeddataservice/app/src/device_kvstore_observer_impl.h new file mode 100644 index 000000000..e292b14ca --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_observer_impl.h @@ -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. + */ + +#ifndef DEVICE_KVSTORE_OBSERVER_IMPL_H +#define DEVICE_KVSTORE_OBSERVER_IMPL_H + +#include "kvstore_observer_impl.h" + +namespace OHOS::DistributedKv { +class DeviceKvStoreObserverImpl : public KvStoreObserverImpl { +public: + DeviceKvStoreObserverImpl(SubscribeType subscribeType, sptr observerProxy, bool deviceSync); + ~DeviceKvStoreObserverImpl() override; + void OnChange(const DistributedDB::KvStoreChangedData &data) override; +private: + void Transfer(const std::list &input, std::list &output, std::string &deviceId); + bool deviceSync_; + std::string localDeviceId_; + sptr observerProxy_; +}; +} +#endif // DEVICE_KVSTORE_OBSERVER_IMPL_H diff --git a/services/distributeddataservice/app/src/device_kvstore_resultset_impl.cpp b/services/distributeddataservice/app/src/device_kvstore_resultset_impl.cpp new file mode 100644 index 000000000..465f14e43 --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_resultset_impl.cpp @@ -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. + */ + +#define LOG_TAG "DevResultSet" + +#include "device_kvstore_resultset_impl.h" +#include "kvstore_utils.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +using namespace AppDistributedKv; + +DeviceKvStoreResultSetImpl::DeviceKvStoreResultSetImpl(DistributedDB::Key tmpKeyPrefix, + DistributedDB::KvStoreResultSet *kvStoreResultSet, bool deviceCoordinate) + : KvStoreResultSetImpl(tmpKeyPrefix, kvStoreResultSet), deviceCoordinate_(deviceCoordinate) +{} + +DeviceKvStoreResultSetImpl::DeviceKvStoreResultSetImpl(DistributedDB::KvStoreResultSet *kvStoreResultSet, + bool deviceCoordinate) : KvStoreResultSetImpl(kvStoreResultSet), deviceCoordinate_(deviceCoordinate) +{} + +DeviceKvStoreResultSetImpl::~DeviceKvStoreResultSetImpl() +{} + +Status DeviceKvStoreResultSetImpl::GetEntry(Entry &entry) +{ + ZLOGD("RS:start"); + if (!deviceCoordinate_) { + ZLOGI("RS: normal"); + return KvStoreResultSetImpl::GetEntry(entry); + } + + Entry tmpEntry; + Status ret = KvStoreResultSetImpl::GetEntry(tmpEntry); + if (ret != Status::SUCCESS) { + return ret; + } + + auto localDevId = KvStoreUtils::GetProviderInstance().GetLocalDevice().deviceId; + if (localDevId.empty()) { + ZLOGI("RS: localDevId empty"); + return Status::ILLEGAL_STATE; + } + + std::vector out; + out.insert(out.end(), tmpEntry.key.Data().begin() + localDevId.size(), tmpEntry.key.Data().end() - sizeof(int)); + entry.key = out; + entry.value = tmpEntry.value; + ZLOGD("RS end."); + return Status::SUCCESS; +} +} \ No newline at end of file diff --git a/services/distributeddataservice/app/src/device_kvstore_resultset_impl.h b/services/distributeddataservice/app/src/device_kvstore_resultset_impl.h new file mode 100755 index 000000000..fc126a51c --- /dev/null +++ b/services/distributeddataservice/app/src/device_kvstore_resultset_impl.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 DEVICE_KVSTORE_RESULTSET_IMPL_H +#define DEVICE_KVSTORE_RESULTSET_IMPL_H + +#include "kvstore_resultset_impl.h" + +namespace OHOS::DistributedKv { +class DeviceKvStoreResultSetImpl : public KvStoreResultSetImpl { +public: + DeviceKvStoreResultSetImpl(DistributedDB::Key tmpKeyPrefix, + DistributedDB::KvStoreResultSet *kvStoreResultSet, bool deviceCoordinate); + DeviceKvStoreResultSetImpl(DistributedDB::KvStoreResultSet *kvStoreResultSet, + bool deviceCoordinate); + ~DeviceKvStoreResultSetImpl(); + Status GetEntry(Entry &entry) override; +private: + bool deviceCoordinate_; +}; +} + +#endif // DEVICE_KVSTORE_RESULTSET_IMPL_H diff --git a/services/distributeddataservice/app/src/flowctrl_manager/BUILD.gn b/services/distributeddataservice/app/src/flowctrl_manager/BUILD.gn new file mode 100755 index 000000000..d76089752 --- /dev/null +++ b/services/distributeddataservice/app/src/flowctrl_manager/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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") + +ohos_static_library("distributeddata_flowctrl_static") { + sources = [ "kvstore_flowctrl_manager.cpp" ] + + include_dirs = [ + "../../../adapter/include/account", + "../../src", + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//third_party/json/single_include", + "//utils/native/base/include", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ "//utils/native/base:utils" ] +} diff --git a/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.cpp b/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.cpp new file mode 100755 index 000000000..fb88eebe6 --- /dev/null +++ b/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreFlowCtrlManager" + +#include "kvstore_flowctrl_manager.h" +#include +#include + +namespace OHOS { +namespace DistributedKv { +const int SECOND_TO_MICROSECOND = 1000; +uint64_t CurrentTimeMicros() +{ + struct timeval tv = { 0, 0 }; + gettimeofday(&tv, nullptr); + return (tv.tv_sec * SECOND_TO_MICROSECOND + tv.tv_usec / SECOND_TO_MICROSECOND); +} + +KvStoreFlowCtrlManager::KvStoreFlowCtrlManager(const int burstCapacity, const int sustainedCapacity) +{ + burstTokenBucket_.maxCapacity = burstCapacity; + burstTokenBucket_.refreshTimeGap = BURST_REFRESH_TIME; + sustainedTokenBucket_.maxCapacity = sustainedCapacity; + sustainedTokenBucket_.refreshTimeGap = SUSTAINED_REFRESH_TIME; +} + +void KvStoreFlowCtrlManager::RefreshTokenBucket(TokenBucket &tokenBucket, uint64_t timeStamp) +{ + tokenBucket.leftNumInTokenBucket = tokenBucket.maxCapacity; + tokenBucket.tokenBucketRefreshTime = timeStamp; +} + +bool KvStoreFlowCtrlManager::IsTokenEnough() +{ + int curTime = CurrentTimeMicros(); + if (IsTokenEnoughSlice(burstTokenBucket_, curTime) && IsTokenEnoughSlice(sustainedTokenBucket_, curTime)) { + burstTokenBucket_.lastAccessTime = curTime; + burstTokenBucket_.leftNumInTokenBucket--; + + sustainedTokenBucket_.lastAccessTime = curTime; + sustainedTokenBucket_.leftNumInTokenBucket--; + return true; + } + return false; +} + +bool KvStoreFlowCtrlManager::IsTokenEnoughSlice(TokenBucket &tokenBucket, uint64_t timeStamp) +{ + // the first time to get token will be allowed; + // if the gap between this time to get token and the least time to fill the bucket + // to the full is larger than 10ms, this operation will be allowed; + if (tokenBucket.tokenBucketRefreshTime == 0 || + timeStamp - tokenBucket.tokenBucketRefreshTime > tokenBucket.refreshTimeGap) { + RefreshTokenBucket(tokenBucket, timeStamp); + return true; + } else { + return tokenBucket.leftNumInTokenBucket >= 1; + } +} +} +} diff --git a/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.h b/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.h new file mode 100755 index 000000000..2967ad7e2 --- /dev/null +++ b/services/distributeddataservice/app/src/flowctrl_manager/kvstore_flowctrl_manager.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 FOUNDATION_KVSTORE_FLOW_CTRL_MANAGER_H +#define FOUNDATION_KVSTORE_FLOW_CTRL_MANAGER_H + +#include + +namespace OHOS { +namespace DistributedKv { +struct TokenBucket { + uint64_t tokenBucketRefreshTime = 0; // last time to refresh the bucket + + uint64_t lastAccessTime = 0; // last time to access + + volatile std::atomic leftNumInTokenBucket {0}; // rest numbers of token in the bucket + + int maxCapacity; // max capacity + + uint64_t refreshTimeGap; // time gap between refreshing +}; + +class KvStoreFlowCtrlManager { +public: + KvStoreFlowCtrlManager() = delete; + + KvStoreFlowCtrlManager(const int burstCapacity, const int sustainedCapacity); + + ~KvStoreFlowCtrlManager() = default; + + bool IsTokenEnough(); + + static const int BURST_REFRESH_TIME = 1000; + + static const int SUSTAINED_REFRESH_TIME = 60000; + +private: + void RefreshTokenBucket(TokenBucket &tokenBucket, uint64_t timeStamp); + + bool IsTokenEnoughSlice(TokenBucket &tokenBucket, uint64_t timeStamp); + + TokenBucket burstTokenBucket_; // token bucket to deal with events in a burst + + TokenBucket sustainedTokenBucket_; // token bucket to deal with sustained events. +}; +} +} + +#endif // FOUNDATION_KVSTORE_FLOW_CTRL_MANAGER_H diff --git a/services/distributeddataservice/app/src/kvstore_account_observer.cpp b/services/distributeddataservice/app/src/kvstore_account_observer.cpp new file mode 100755 index 000000000..4572d07dd --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_account_observer.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreAccountObserver" + +#include "kvstore_account_observer.h" +#include "log_print.h" +#include +namespace OHOS { +namespace DistributedKv { +std::atomic g_kvStoreAccountEventStatus {0}; +void KvStoreAccountObserver::OnAccountChanged(const AccountEventInfo &eventInfo) +{ + ZLOGI("account event %d, begin.", eventInfo.status); + kvStoreDataService_.AccountEventChanged(eventInfo); + ZLOGI("account event %d, end.", eventInfo.status); +} +} // namespace DistributedKv +} // namespace OHOS \ No newline at end of file diff --git a/services/distributeddataservice/app/src/kvstore_account_observer.h b/services/distributeddataservice/app/src/kvstore_account_observer.h new file mode 100755 index 000000000..782167c32 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_account_observer.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 KVSTORE_ACCOUNT_OBSERVER_H +#define KVSTORE_ACCOUNT_OBSERVER_H + +#include "kvstore_data_service.h" +#include "account_delegate.h" +#include + +namespace OHOS { +namespace DistributedKv { +// KvStore account event proc controller. +extern std::atomic g_kvStoreAccountEventStatus; +#define KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(result) \ +do { \ + if (g_kvStoreAccountEventStatus == 1) { \ + ZLOGW("system is busy with processing account event."); \ + } \ +} while (0) + +class KvStoreAccountObserver : public AccountDelegate::Observer { +public: + explicit KvStoreAccountObserver(KvStoreDataService &kvStoreDataService) + : kvStoreDataService_(kvStoreDataService) {} + ~KvStoreAccountObserver() override = default; + + void OnAccountChanged(const AccountEventInfo &eventInfo) override; + // must specify unique name for observer + std::string Name() override + { + return "DistributedDataService"; + } + +private: + KvStoreDataService &kvStoreDataService_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_ACCOUNT_OBSERVER_H diff --git a/services/distributeddataservice/app/src/kvstore_app_accessor.cpp b/services/distributeddataservice/app/src/kvstore_app_accessor.cpp new file mode 100755 index 000000000..57f262340 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_app_accessor.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreAppAccessor" + +#include "kvstore_app_accessor.h" +#include +#include +#include "broadcast_sender.h" +#include "kv_store_delegate_manager.h" +#include "kvstore_app_manager.h" +#include "kvstore_meta_manager.h" +#include "log_print.h" +#include "permission_validator.h" + +namespace OHOS::DistributedKv { +KvStoreAppAccessor::~KvStoreAppAccessor() +{ + ZLOGD("destructor."); +} + +KvStoreAppAccessor::KvStoreAppAccessor() +{ + ZLOGD("constructor."); +} + +KvStoreAppAccessor &KvStoreAppAccessor::GetInstance() +{ + static KvStoreAppAccessor appAccessor; + return appAccessor; +} + +void KvStoreAppAccessor::EnableKvStoreAutoLaunch(const AppAccessorParam ¶m) +{ + if (!PermissionValidator::IsAutoLaunchEnabled(param.appId)) { + ZLOGI("AppId:%s is not allowed.", param.appId.c_str()); + return; + } + + auto callback = std::bind(&KvStoreAppAccessor::OnCallback, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4); + auto dbStatus = DistributedDB::KvStoreDelegateManager::EnableKvStoreAutoLaunch(param.userId, param.appId, + param.storeId, param.launchOption, + callback); + if (dbStatus == DistributedDB::DBStatus::OK || dbStatus == DistributedDB::DBStatus::ALREADY_SET) { + ZLOGI("AppId:%s enable auto launch success.", param.appId.c_str()); + return; + } + ZLOGW("AppId:%s enable auto launch failed.", param.appId.c_str()); +} + +void KvStoreAppAccessor::EnableKvStoreAutoLaunch() +{ + ZLOGI("start"); + std::map entries; + if (KvStoreMetaManager::GetInstance().GetFullMetaData(entries)) { + for (auto &meta : entries) { + KvStoreMetaData &metaData = meta.second.kvStoreMetaData; + ZLOGI("meta appId:%s", metaData.appId.c_str()); + if (!PermissionValidator::IsAutoLaunchEnabled(metaData.appId)) { + continue; + } + DistributedDB::CipherPassword password; + const std::vector &secretKey = meta.second.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Get secret key failed."); + return; + } + auto pathType = KvStoreAppManager::ConvertPathType(metaData.bundleName, metaData.securityLevel); + std::string appPath = KvStoreAppManager::GetDataStoragePath( + metaData.deviceAccountId, metaData.bundleName, pathType); + DistributedDB::AutoLaunchOption dbLaunchOption; + dbLaunchOption.createIfNecessary = true; + dbLaunchOption.createDirByStoreIdOnly = true; + dbLaunchOption.dataDir = Constant::Concatenate({appPath, "/", metaData.bundleName}); + dbLaunchOption.observer = nullptr; + if (password.GetSize() > 0) { + dbLaunchOption.isEncryptedDb = true; + dbLaunchOption.cipher = DistributedDB::CipherType::AES_256_GCM; + dbLaunchOption.passwd = password; + } + dbLaunchOption.secOption = KvStoreAppManager::ConvertSecurity(metaData.securityLevel); + EnableKvStoreAutoLaunch({metaData.userId, metaData.appId, metaData.storeId, dbLaunchOption}); + } + } else { + ZLOGW("Init Service start enable failed."); + } +} + +void KvStoreAppAccessor::DisableKvStoreAutoLaunch(const AppAccessorParam ¶m) +{ +} + +void KvStoreAppAccessor::OnCallback(const std::string &userId, const std::string &appId, const std::string &storeId, + const DistributedDB::AutoLaunchStatus status) +{ + ZLOGI("start"); + if (status == DistributedDB::AutoLaunchStatus::WRITE_OPENED) { + KvStoreMetaData kvStoreMetaData; + if (KvStoreMetaManager::GetInstance().GetKvStoreMetaDataByAppId(appId, kvStoreMetaData)) { + BroadcastSender::GetInstance()->SendEvent({userId, kvStoreMetaData.bundleName, storeId}); + ZLOGW("AppId:%s is opened, bundle:%s.", appId.c_str(), kvStoreMetaData.bundleName.c_str()); + } else { + ZLOGW("AppId:%s open failed.", appId.c_str()); + } + } else { + ZLOGD("AppId:%s is closed.", appId.c_str()); + } +} +} diff --git a/services/distributeddataservice/app/src/kvstore_app_accessor.h b/services/distributeddataservice/app/src/kvstore_app_accessor.h new file mode 100755 index 000000000..481da44a5 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_app_accessor.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 KVSTORE_APP_ACCESSOR_H +#define KVSTORE_APP_ACCESSOR_H + +#include "kv_store_delegate_manager.h" +#include +#include +#include + +namespace OHOS::DistributedKv { +struct AppAccessorParam { + std::string userId; + std::string appId; + std::string storeId; + DistributedDB::AutoLaunchOption launchOption; +}; + +class KvStoreAppAccessor { +public: + ~KvStoreAppAccessor(); + void EnableKvStoreAutoLaunch(const AppAccessorParam ¶m); + + void DisableKvStoreAutoLaunch(const AppAccessorParam ¶m); + + void EnableKvStoreAutoLaunch(); + + void OnCallback(const std::string &userId, const std::string &appId, + const std::string &storeId, DistributedDB::AutoLaunchStatus status); + static KvStoreAppAccessor &GetInstance(); +private: + KvStoreAppAccessor(); +}; +} +#endif // KVSTORE_APP_ACCESSOR_H diff --git a/services/distributeddataservice/app/src/kvstore_app_manager.cpp b/services/distributeddataservice/app/src/kvstore_app_manager.cpp new file mode 100755 index 000000000..002ed44bc --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_app_manager.cpp @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreAppManager" + +#include "kvstore_app_manager.h" +#include +#include +#include +#include +#include +#include "account_delegate.h" +#include "broadcast_sender.h" +#include "constant.h" +#include "directory_utils.h" +#include "device_kvstore_impl.h" +#include "ikvstore.h" +#include "kv_store_delegate.h" +#include "kvstore_app_accessor.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "process_communicator_impl.h" +#include "permission_validator.h" +#include "reporter.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreAppManager::KvStoreAppManager(const std::string &bundleName, const std::string &deviceAccountId) + : bundleName_(bundleName), deviceAccountId_(deviceAccountId), flowCtrlManager_(BURST_CAPACITY, SUSTAINED_CAPACITY) +{ + ZLOGI("begin."); + GetDelegateManager(PATH_DE); + GetDelegateManager(PATH_CE); +} + +KvStoreAppManager::~KvStoreAppManager() +{ + ZLOGD("begin."); + stores_[PATH_DE].clear(); + stores_[PATH_CE].clear(); + + { + std::lock_guard guard(delegateMutex_); + delete delegateManagers_[PATH_DE]; + delete delegateManagers_[PATH_CE]; + delegateManagers_[PATH_DE] = nullptr; + delegateManagers_[PATH_CE] = nullptr; + } +} + +Status KvStoreAppManager::ConvertErrorStatus(DistributedDB::DBStatus dbStatus, bool createIfMissing) +{ + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error: %d.", static_cast(dbStatus)); + switch (dbStatus) { + case DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB: + return Status::CRYPT_ERROR; + case DistributedDB::DBStatus::SCHEMA_MISMATCH: + return Status::SCHEMA_MISMATCH; + case DistributedDB::DBStatus::INVALID_SCHEMA: + return Status::INVALID_SCHEMA; + case DistributedDB::DBStatus::NOT_SUPPORT: + return Status::NOT_SUPPORT; + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: + break; + } + if (createIfMissing) { + return Status::DB_ERROR; + } else { + return Status::STORE_NOT_FOUND; + } + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::GetKvStore(const Options &options, const std::string &storeId, + const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin"); + PathType type = ConvertPathType(bundleName_, options.securityLevel); + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManagers[%d] is nullptr.", type); + callback(nullptr); + return Status::ILLEGAL_STATE; + } + + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + std::lock_guard lg(storeMutex_); + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + sptr kvStoreImpl = it->second; + ZLOGI("find store in map refcount: %d.", kvStoreImpl->GetSptrRefCount()); + static_cast(kvStoreImpl.GetRefPtr())->IncreaseOpenCount(); + callback(std::move(kvStoreImpl)); + return Status::SUCCESS; + } + + if ((GetTotalKvStoreNum()) >= static_cast(Constant::MAX_OPEN_KVSTORES)) { + ZLOGE("limit %d KvStores can be opened.", Constant::MAX_OPEN_KVSTORES); + callback(nullptr); + return Status::ERROR; + } + + DistributedDB::KvStoreDelegate::Option dbOption; + auto status = InitDbOption(options, cipherKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitDbOption failed."); + callback(nullptr); + return status; + } + + DistributedDB::KvStoreDelegate *storeDelegate = nullptr; + DistributedDB::DBStatus dbStatusTmp; + delegateManager->GetKvStore(storeId, dbOption, + [&storeDelegate, &dbStatusTmp](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreDelegate *delegate) { + storeDelegate = delegate; + dbStatusTmp = dbStatus; + }); + + if (storeDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr, status:%d.", static_cast(dbStatusTmp)); + callback(nullptr); + return ConvertErrorStatus(dbStatusTmp, options.createIfMissing); + } + + ZLOGD("get delegate"); + sptr store = new (std::nothrow)KvStoreImpl(options, deviceAccountId_, bundleName_, + storeId, GetDbDir(options), storeDelegate); + if (store == nullptr) { + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + auto result = stores_[type].emplace(storeId, store); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + + sptr kvStoreImpl = result.first->second; + ZLOGD("after emplace refcount: %d", kvStoreImpl->GetSptrRefCount()); + callback(std::move(kvStoreImpl)); + return Status::SUCCESS; +} + +Status KvStoreAppManager::GetKvStore(const Options &options, const std::string &storeId, + const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin"); + PathType type = ConvertPathType(bundleName_, options.securityLevel); + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManagers[%d] is nullptr.", type); + callback(nullptr); + return Status::ILLEGAL_STATE; + } + + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + std::lock_guard lg(storeMutex_); + auto it = singleStores_[type].find(storeId); + if (it != singleStores_[type].end()) { + sptr singleKvStoreImpl = it->second; + ZLOGI("find store in map refcount: %d.", singleKvStoreImpl->GetSptrRefCount()); + static_cast(singleKvStoreImpl.GetRefPtr())->IncreaseOpenCount(); + callback(std::move(singleKvStoreImpl)); + return Status::SUCCESS; + } + + if ((GetTotalKvStoreNum()) >= static_cast(Constant::MAX_OPEN_KVSTORES)) { + ZLOGE("limit %d KvStores can be opened.", Constant::MAX_OPEN_KVSTORES); + callback(nullptr); + return Status::ERROR; + } + + DistributedDB::KvStoreNbDelegate::Option dbOption; + auto status = InitNbDbOption(options, cipherKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitNbDbOption failed."); + callback(nullptr); + return status; + } + + DistributedDB::KvStoreNbDelegate *storeDelegate = nullptr; + DistributedDB::DBStatus dbStatusTmp; + delegateManager->GetKvStore(storeId, dbOption, + [&](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreNbDelegate *kvStoreDelegate) { + storeDelegate = kvStoreDelegate; + dbStatusTmp = dbStatus; + }); + + if (storeDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr."); + callback(nullptr); + return ConvertErrorStatus(dbStatusTmp, options.createIfMissing); + } + std::string kvStorePath = GetDbDir(options); + auto store = new (std::nothrow) DeviceKvStoreImpl({ + options, options.kvStoreType == KvStoreType::DEVICE_COLLABORATION, deviceAccountId_, bundleName_, storeId, + kvStorePath}, storeDelegate); + if (store == nullptr) { + ZLOGE("store is nullptr."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + auto result = singleStores_[type].emplace(storeId, store); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + delete store; + return Status::ERROR; + } + + sptr singleKvStoreImpl = result.first->second; + ZLOGI("after emplace refcount: %d autoSync: %d", + singleKvStoreImpl->GetSptrRefCount(), static_cast(options.autoSync)); + if (options.autoSync) { + bool autoSync = true; + DistributedDB::PragmaData data = static_cast(&autoSync); + auto pragmaStatus = storeDelegate->Pragma(DistributedDB::PragmaCmd::AUTO_SYNC, data); + if (pragmaStatus != DistributedDB::DBStatus::OK) { + ZLOGE("pragmaStatus: %d", static_cast(pragmaStatus)); + } + } + + callback(std::move(singleKvStoreImpl)); + DistributedDB::AutoLaunchOption launchOption = { + options.createIfMissing, options.encrypt, dbOption.cipher, dbOption.passwd, dbOption.schema, + dbOption.createDirByStoreIdOnly, kvStorePath, nullptr + }; + launchOption.secOption = ConvertSecurity(options.securityLevel); + AppAccessorParam accessorParam = {Constant::DEFAULT_GROUP_ID, trueAppId_, storeId, launchOption}; + KvStoreAppAccessor::GetInstance().EnableKvStoreAutoLaunch(accessorParam); + return Status::SUCCESS; +} + +Status KvStoreAppManager::CloseKvStore(const std::string &storeId) +{ + ZLOGI("CloseKvStore"); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + std::lock_guard lg(storeMutex_); + Status status = CloseKvStore(storeId, PATH_DE); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + + status = CloseKvStore(storeId, PATH_CE); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + + ZLOGW("store not open"); + return Status::STORE_NOT_OPEN; +} + +Status KvStoreAppManager::CloseAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(storeMutex_); + if (GetTotalKvStoreNum() == 0) { + return Status::STORE_NOT_OPEN; + } + + ZLOGI("close %zu KvStores.", GetTotalKvStoreNum()); + Status status = CloseAllKvStore(PATH_DE); + if (status == Status::DB_ERROR) { + return status; + } + status = CloseAllKvStore(PATH_CE); + if (status == Status::DB_ERROR) { + return status; + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteKvStore(const std::string &storeId) +{ + ZLOGI("%s", storeId.c_str()); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + Status statusDE = DeleteKvStore(storeId, PATH_DE); + Status statusCE = DeleteKvStore(storeId, PATH_CE); + if (statusDE == Status::SUCCESS || statusCE == Status::SUCCESS) { + return Status::SUCCESS; + } + + ZLOGE("delegate close error."); + return Status::DB_ERROR; +} + +Status KvStoreAppManager::DeleteAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(storeMutex_); + if (GetTotalKvStoreNum() == 0) { + return Status::STORE_NOT_OPEN; + } + ZLOGI("delete %d KvStores.", int32_t(GetTotalKvStoreNum())); + + Status status = DeleteAllKvStore(PATH_DE); + if (status != Status::SUCCESS) { + ZLOGE("path de delegate delete error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + status = DeleteAllKvStore(PATH_CE); + if (status != Status::SUCCESS) { + ZLOGE("path ce delegate delete error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::MigrateAllKvStore(const std::string &harmonyAccountId) +{ + ZLOGI("begin"); + if (PermissionValidator::IsAutoLaunchEnabled(bundleName_)) { + return Status::SUCCESS; + } + + std::lock_guard lg(storeMutex_); + // update userid in kvstore tuple map of permission adapter. + KvStoreTuple srcKvStoreTuple {userId_, bundleName_}; + KvStoreTuple dstKvStoreTuple {harmonyAccountId, bundleName_}; + PermissionValidator::UpdateKvStoreTupleMap(srcKvStoreTuple, dstKvStoreTuple); + userId_ = harmonyAccountId; + ZLOGI("path de migration begin."); + Status statusDE = MigrateAllKvStore(harmonyAccountId, PATH_DE); + ZLOGI("path ce migration begin."); + Status statusCE = MigrateAllKvStore(harmonyAccountId, PATH_CE); + return (statusCE != Status::SUCCESS) ? statusCE : statusDE; +} + +Status KvStoreAppManager::InitDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreDelegate::Option &dbOption) +{ + DistributedDB::CipherPassword password; + auto status = password.SetValue(cipherKey.data(), cipherKey.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + dbOption.createIfNecessary = options.createIfMissing; + dbOption.localOnly = false; + dbOption.isEncryptedDb = options.encrypt; + if (options.encrypt) { + dbOption.cipher = DistributedDB::CipherType::AES_256_GCM; + dbOption.passwd = password; + } + dbOption.createDirByStoreIdOnly = options.dataOwnership; + return Status::SUCCESS; +} + +Status KvStoreAppManager::InitNbDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreNbDelegate::Option &dbOption) +{ + DistributedDB::CipherPassword password; + auto status = password.SetValue(cipherKey.data(), cipherKey.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + + dbOption.createIfNecessary = options.createIfMissing; + dbOption.isEncryptedDb = options.encrypt; + if (options.encrypt) { + dbOption.cipher = DistributedDB::CipherType::AES_256_GCM; + dbOption.passwd = password; + } + + if (options.kvStoreType == KvStoreType::SINGLE_VERSION) { + dbOption.conflictResolvePolicy = DistributedDB::LAST_WIN; + } else if (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION) { + dbOption.conflictResolvePolicy = DistributedDB::DEVICE_COLLABORATION; + } else { + ZLOGE("kvStoreType is invalid"); + return Status::INVALID_ARGUMENT; + } + + dbOption.schema = options.schema; + dbOption.createDirByStoreIdOnly = options.dataOwnership; + dbOption.secOption = ConvertSecurity(options.securityLevel); + return Status::SUCCESS; +} + +std::string KvStoreAppManager::GetDbDir(const Options &options) const +{ + return GetDataStoragePath(deviceAccountId_, bundleName_, ConvertPathType(bundleName_, options.securityLevel)); +} + +KvStoreAppManager::PathType KvStoreAppManager::ConvertPathType(const std::string &bundleName, int securityLevel) +{ + PathType type = PATH_CE; + if ((securityLevel == NO_LABEL && PermissionValidator::IsSystemService(bundleName)) || + securityLevel == S0 || + securityLevel == S1) { + type = PATH_DE; + } + return type; +} + +DistributedDB::KvStoreDelegateManager *KvStoreAppManager::GetDelegateManager(PathType type) +{ + std::lock_guard guard(delegateMutex_); + if (delegateManagers_[type] != nullptr) { + return delegateManagers_[type]; + } + + std::string directory = GetDataStoragePath(deviceAccountId_, bundleName_, type); + bool ret = ForceCreateDirectory(directory); + if (!ret) { + ZLOGE("create directory[%s] failed, errstr=[%d].", directory.c_str(), errno); + return nullptr; + } + // change mode for directories to 0755, and for files to 0600. + DirectoryUtils::ChangeModeDirOnly(directory, Constant::DEFAULT_MODE_DIR); + DirectoryUtils::ChangeModeFileOnly(directory, Constant::DEFAULT_MODE_FILE); + + trueAppId_ = KvStoreUtils::GetAppIdByBundleName(bundleName_); + if (trueAppId_.empty()) { + delegateManagers_[type] = nullptr; + ZLOGW("trueAppId_ empty(permission issues?)"); + return nullptr; + } + + userId_ = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(bundleName_); + ZLOGD("accountId: %s bundleName: %s", KvStoreUtils::ToBeAnonymous(userId_).c_str(), bundleName_.c_str()); + delegateManagers_[type] = new (std::nothrow) DistributedDB::KvStoreDelegateManager(trueAppId_, userId_); + if (delegateManagers_[type] == nullptr) { + ZLOGE("delegateManagers_[%d] is nullptr.", type); + return nullptr; + } + + DistributedDB::KvStoreConfig kvStoreConfig; + kvStoreConfig.dataDir = directory; + delegateManagers_[type]->SetKvStoreConfig(kvStoreConfig); + DistributedDB::KvStoreDelegateManager::SetProcessLabel(Constant::PROCESS_LABEL, "default"); + auto communicator = std::make_shared(); + auto result = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + ZLOGI("app set communicator result:%d.", static_cast(result)); + return delegateManagers_[type]; +} + +DistributedDB::KvStoreDelegateManager *KvStoreAppManager::SwitchDelegateManager(PathType type, + DistributedDB::KvStoreDelegateManager *delegateManager) +{ + std::lock_guard guard(delegateMutex_); + DistributedDB::KvStoreDelegateManager *oldDelegateManager = delegateManagers_[type]; + delegateManagers_[type] = delegateManager; + return oldDelegateManager; +} + +Status KvStoreAppManager::CloseKvStore(const std::string &storeId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + ZLOGD("find store and close delegate."); + InnerStatus status = it->second->Close(delegateManager); + if (status == InnerStatus::SUCCESS) { + stores_[type].erase(it); + return Status::SUCCESS; + } + if (status == InnerStatus::DECREASE_REFCOUNT) { + return Status::SUCCESS; + } + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + auto itSingle = singleStores_[type].find(storeId); + if (itSingle != singleStores_[type].end()) { + ZLOGD("find single store and close delegate."); + InnerStatus status = itSingle->second->Close(delegateManager); + if (status == InnerStatus::SUCCESS) { + singleStores_[type].erase(itSingle); + return Status::SUCCESS; + } + if (status == InnerStatus::DECREASE_REFCOUNT) { + return Status::SUCCESS; + } + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + return Status::STORE_NOT_OPEN; +} + +Status KvStoreAppManager::CloseAllKvStore(PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + for (auto it = stores_[type].begin(); it != stores_[type].end(); it = stores_[type].erase(it)) { + KvStoreImpl *currentStore = it->second.GetRefPtr(); + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + } + stores_[type].clear(); + + for (auto it = singleStores_[type].begin(); it != singleStores_[type].end(); it = singleStores_[type].erase(it)) { + SingleKvStoreImpl *currentStore = it->second.GetRefPtr(); + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + } + singleStores_[type].clear(); + return Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteKvStore(const std::string &storeId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + std::lock_guard lg(storeMutex_); + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + Status status = it->second->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + return Status::DB_ERROR; + } + stores_[type].erase(it); + } + + auto itSingle = singleStores_[type].find(storeId); + if (itSingle != singleStores_[type].end()) { + Status status = itSingle->second->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + return Status::DB_ERROR; + } + singleStores_[type].erase(itSingle); + } + + DistributedDB::DBStatus status = delegateManager->DeleteKvStore(storeId); + if (singleStores_[type].empty() && stores_[type].empty()) { + SwitchDelegateManager(type, nullptr); + delete delegateManager; + } + return (status != DistributedDB::DBStatus::OK) ? Status::DB_ERROR : Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteAllKvStore(PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + for (auto it = stores_[type].begin(); it != stores_[type].end(); it = stores_[type].erase(it)) { + std::string storeId = it->first; + KvStoreImpl *currentStore = it->second.GetRefPtr(); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate delete close failed error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + ZLOGI("delete kvstore, refcount %d.", it->second->GetSptrRefCount()); + DistributedDB::DBStatus dbStatus = delegateManager->DeleteKvStore(storeId); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate delete error: %d.", static_cast(dbStatus)); + return Status::DB_ERROR; + } + } + stores_[type].clear(); + + for (auto it = singleStores_[type].begin(); it != singleStores_[type].end(); it = singleStores_[type].erase(it)) { + std::string storeId = it->first; + SingleKvStoreImpl *currentStore = it->second.GetRefPtr(); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate delete close failed error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + DistributedDB::DBStatus dbStatus = delegateManager->DeleteKvStore(storeId); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate delete error: %d.", static_cast(dbStatus)); + return Status::DB_ERROR; + } + } + singleStores_[type].clear(); + SwitchDelegateManager(type, nullptr); + delete delegateManager; + return Status::SUCCESS; +} + +Status KvStoreAppManager::MigrateAllKvStore(const std::string &harmonyAccountId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager is nullptr."); + return Status::ILLEGAL_STATE; + } + + std::string dirPath = GetDataStoragePath(deviceAccountId_, bundleName_, type); + DistributedDB::KvStoreDelegateManager *newDelegateManager = nullptr; + Status status = Status::SUCCESS; + ZLOGI("KvStore migration begin."); + for (auto &it : stores_[type]) { + sptr impl = it.second; + if (impl->MigrateKvStore(harmonyAccountId, dirPath, delegateManager, newDelegateManager) != Status::SUCCESS) { + status = Status::MIGRATION_KVSTORE_FAILED; + ZLOGE("migrate kvstore for appId-%s failed.", bundleName_.c_str()); + // skip this failed, continue to migrate other kvstore. + } + } + + ZLOGI("SingleKvStore migration begin."); + for (auto &it : singleStores_[type]) { + sptr impl = it.second; + if (impl->MigrateKvStore(harmonyAccountId, dirPath, delegateManager, newDelegateManager) != Status::SUCCESS) { + status = Status::MIGRATION_KVSTORE_FAILED; + ZLOGE("migrate single kvstore for appId-%s failed.", bundleName_.c_str()); + // skip this failed, continue to migrate other kvstore. + } + } + + if (newDelegateManager != nullptr) { + delegateManager = SwitchDelegateManager(type, newDelegateManager); + delete delegateManager; + } + return status; +} + +size_t KvStoreAppManager::GetTotalKvStoreNum() const +{ + size_t total = stores_[PATH_DE].size(); + total += stores_[PATH_CE].size(); + total += singleStores_[PATH_DE].size(); + total += singleStores_[PATH_CE].size(); + return int(total); +}; + +std::string KvStoreAppManager::GetDataStoragePath(const std::string &deviceAccountId, const std::string &bundleName, + PathType type) +{ + std::string miscPath = (type == PATH_DE) ? Constant::ROOT_PATH_DE : Constant::ROOT_PATH_CE; + return Constant::Concatenate({ + miscPath, "/", Constant::SERVICE_NAME, "/", deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), + "/", bundleName, "/" + }); +} + +DistributedDB::SecurityOption KvStoreAppManager::ConvertSecurity(int securityLevel) +{ + if (securityLevel < SecurityLevel::NO_LABEL || securityLevel > SecurityLevel::S4) { + return {DistributedDB::NOT_SET, DistributedDB::ECE}; + } + switch (securityLevel) { + case SecurityLevel::S3: + return {DistributedDB::S3, DistributedDB::SECE}; + case SecurityLevel::S4: + return {DistributedDB::S4, DistributedDB::ECE}; + default: + return {securityLevel, DistributedDB::ECE}; + } +} + +void KvStoreAppManager::Dump(int fd) const +{ + const std::string prefix(8, ' '); + std::string dePath = GetDataStoragePath(deviceAccountId_, bundleName_, PATH_DE); + std::string cePath = GetDataStoragePath(deviceAccountId_, bundleName_, PATH_CE); + size_t singleStoreNum = singleStores_[PATH_DE].size() + singleStores_[PATH_CE].size(); + dprintf(fd, "%s----------------------------------------------------------\n", prefix.c_str()); + dprintf(fd, "%sAppID : %s\n", prefix.c_str(), trueAppId_.c_str()); + dprintf(fd, "%sBundleName : %s\n", prefix.c_str(), bundleName_.c_str()); + dprintf(fd, "%sAppDEDirectory: %s\n", prefix.c_str(), dePath.c_str()); + dprintf(fd, "%sAppCEDirectory: %s\n", prefix.c_str(), cePath.c_str()); + dprintf(fd, "%sStore count : %u\n", prefix.c_str(), static_cast(singleStoreNum)); + for (const auto &singleStoreMap : singleStores_) { + for (const auto &pair : singleStoreMap) { + pair.second->OnDump(fd); + } + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_app_manager.h b/services/distributeddataservice/app/src/kvstore_app_manager.h new file mode 100755 index 000000000..82d559c59 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_app_manager.h @@ -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. + */ + +#ifndef KV_STORE_APP_MANAGER_H +#define KV_STORE_APP_MANAGER_H + +#include +#include +#include +#include "flowctrl_manager/kvstore_flowctrl_manager.h" +#include "kv_store_delegate_manager.h" +#include "kvstore_impl.h" +#include "types.h" +#include "single_kvstore_impl.h" +#include "kv_store_nb_delegate.h" +#include "nocopyable.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreAppManager { +public: + enum PathType { + PATH_DE, + PATH_CE, + PATH_TYPE_MAX + }; + + KvStoreAppManager(const std::string &bundleName, const std::string &deviceAccountId); + + virtual ~KvStoreAppManager(); + + Status GetKvStore(const Options &options, const std::string &storeId, const std::vector &cipherKey, + std::function)> callback); + + Status GetKvStore(const Options &options, const std::string &storeId, const std::vector &cipherKey, + std::function)> callback); + + Status CloseKvStore(const std::string &storeId); + + Status CloseAllKvStore(); + + Status DeleteKvStore(const std::string &storeId); + + Status DeleteAllKvStore(); + + Status MigrateAllKvStore(const std::string &harmonyAccountId); + + static Status InitDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreDelegate::Option &dbOption); + + static Status InitNbDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreNbDelegate::Option &dbOption); + + static std::string GetDataStoragePath(const std::string &deviceAccountId, const std::string &bundleName, + PathType type); + + static PathType ConvertPathType(const std::string &bundleName, int securityLevel); + + std::string GetDbDir(const Options &options) const; + + void Dump(int fd) const; + + static DistributedDB::SecurityOption ConvertSecurity(int securityLevel); + + size_t GetTotalKvStoreNum() const; +private: + DISALLOW_COPY_AND_MOVE(KvStoreAppManager); + Status ConvertErrorStatus(DistributedDB::DBStatus dbStatus, bool createIfMissing); + DistributedDB::KvStoreDelegateManager *GetDelegateManager(PathType type); + DistributedDB::KvStoreDelegateManager *SwitchDelegateManager(PathType type, + DistributedDB::KvStoreDelegateManager *delegateManager); + Status CloseKvStore(const std::string &storeId, PathType type); + Status CloseAllKvStore(PathType type); + Status DeleteKvStore(const std::string &storeId, PathType type); + Status DeleteAllKvStore(PathType type); + Status MigrateAllKvStore(const std::string &harmonyAccountId, PathType type); + std::mutex storeMutex_ {}; + std::map> stores_[PATH_TYPE_MAX] {}; + std::map> singleStores_[PATH_TYPE_MAX] {}; + std::string userId_ {}; + std::string bundleName_ {}; + std::string deviceAccountId_ {}; + std::string trueAppId_ {}; + std::mutex delegateMutex_ {}; + DistributedDB::KvStoreDelegateManager *delegateManagers_[PATH_TYPE_MAX] {nullptr, nullptr}; + KvStoreFlowCtrlManager flowCtrlManager_; + static inline const int BURST_CAPACITY = 50; + static inline const int SUSTAINED_CAPACITY = 500; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KV_STORE_APP_MANAGER_H diff --git a/services/distributeddataservice/app/src/kvstore_common.h b/services/distributeddataservice/app/src/kvstore_common.h new file mode 100755 index 000000000..41d4eb139 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_common.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 KVSTORE_COMMON_H +#define KVSTORE_COMMON_H + +#include "types.h" + +namespace OHOS::DistributedKv { +struct KvStoreParams { + Options options; + bool deviceCoordinate; + std::string deviceAccountId; + std::string appId; + std::string storeId; + std::string appDirectory; +}; +} +#endif // KVSTORE_COMMON_H diff --git a/services/distributeddataservice/app/src/kvstore_data_service.cpp b/services/distributeddataservice/app/src/kvstore_data_service.cpp new file mode 100644 index 000000000..70b440067 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_data_service.cpp @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreDataService" + +#include "kvstore_data_service.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "auto_launch_export.h" +#include "communication_provider.h" +#include "constant.h" +#include "crypto_utils.h" +#include "dds_trace.h" +#include "device_change_listener_impl.h" +#include "device_kvstore_impl.h" +#include "kvstore_account_observer.h" +#include "kvstore_app_accessor.h" +#include "kvstore_meta_manager.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "permission_validator.h" +#include "process_communicator_impl.h" +#include "reporter.h" +#include "if_system_ability_manager.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "uninstaller/uninstaller.h" + +namespace OHOS { +namespace DistributedKv { +using json = nlohmann::json; +using namespace std::chrono; + +REGISTER_SYSTEM_ABILITY_BY_ID(KvStoreDataService, DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID, true); + +constexpr size_t MAX_APP_ID_LENGTH = 256; + +KvStoreDataService::KvStoreDataService(bool runOnCreate) + : SystemAbility(runOnCreate), + accountMutex_(), + deviceAccountMap_(), + clientDeathObserverMutex_(), + clientDeathObserverMap_() +{ + ZLOGI("begin."); + Initialize(); +} + +KvStoreDataService::KvStoreDataService(int32_t systemAbilityId, bool runOnCreate) + : SystemAbility(systemAbilityId, runOnCreate), + accountMutex_(), + deviceAccountMap_(), + clientDeathObserverMutex_(), + clientDeathObserverMap_() +{ + ZLOGI("begin"); + Initialize(); +} + +KvStoreDataService::~KvStoreDataService() +{ + ZLOGI("begin."); + deviceAccountMap_.clear(); +} + +void KvStoreDataService::Initialize() +{ + ZLOGI("begin."); + KvStoreMetaManager::GetInstance().InitMetaParameter(); + std::thread th = std::thread([]() { + DistributedDB::KvStoreDelegateManager::SetProcessLabel(Constant::PROCESS_LABEL, "default"); + auto communicator = std::make_shared(); + auto ret = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + ZLOGI("set communicator ret:%d.", static_cast(ret)); + if (KvStoreMetaManager::GetInstance().CheckRootKeyExist() == Status::SUCCESS) { + return; + } + constexpr int RETRY_MAX_TIMES = 100; + int retryCount = 0; + constexpr int RETRY_TIME_INTERVAL_MILLISECOND = 1 * 1000 * 1000; // retry after 1 second + while (retryCount < RETRY_MAX_TIMES) { + if (KvStoreMetaManager::GetInstance().GenerateRootKey() == Status::SUCCESS) { + ZLOGI("GenerateRootKey success."); + break; + } + retryCount++; + ZLOGE("GenerateRootKey failed."); + usleep(RETRY_TIME_INTERVAL_MILLISECOND); + } + }); + th.detach(); + + accountEventObserver_ = std::make_shared(*this); + AccountDelegate::GetInstance()->Subscribe(accountEventObserver_); +} + +bool KvStoreDataService::CheckBundleName(const std::string &bundleName) const +{ + if (bundleName.empty() || bundleName.size() > MAX_APP_ID_LENGTH || + bundleName.find(Constant::KEY_SEPARATOR) != std::string::npos) { + return false; + } + + auto iter = std::find_if_not(bundleName.begin(), bundleName.end(), + [](char c) { return (std::isprint(c) && c != '/'); }); + + return (iter == bundleName.end()); +} + +bool KvStoreDataService::CheckStoreId(const std::string &storeId) const +{ + if (storeId.empty() || storeId.size() > Constant::MAX_STORE_ID_LENGTH || + storeId.find(Constant::KEY_SEPARATOR) != std::string::npos) { + return false; + } + + auto iter = std::find_if_not(storeId.begin(), storeId.end(), + [](char c) { return (std::isdigit(c) || std::isalpha(c) || c == '_'); }); + + return (iter == storeId.end()); +} + +Status KvStoreDataService::GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (callback == nullptr) { + ZLOGW("callback is nullptr"); + return Status::ERROR; + } + if (appId.appId.empty() || storeId.storeId.empty()) { + ZLOGW("appid or storeid empty"); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KvStoreType kvStoreType = options.kvStoreType; + if (kvStoreType != KvStoreType::DEVICE_COLLABORATION && kvStoreType != KvStoreType::SINGLE_VERSION && + kvStoreType != KvStoreType::MULTI_VERSION) { + ZLOGE("invalid kvStore type."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGW("appId empty(permission issues?)"); + callback(nullptr); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + callback(nullptr); + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + if (!CheckOptions(options, metaKey)) { + callback(nullptr); + ZLOGE("encrypt type or kvStore type is not the same"); + return Status::INVALID_ARGUMENT; + } + std::vector secretKey; + std::unique_ptr, void (*)(std::vector *)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + + bool outdated = false; + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp, "KEY"); + auto secretKeyFile = KvStoreMetaManager::GetSecretKeyFile(deviceAccountId, bundleName, storeIdTmp); + Status alreadyCreated = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaSecretKey, CHECK_EXIST_LOCAL); + if (options.encrypt) { + ZLOGI("Getting secret key"); + if (alreadyCreated != Status::SUCCESS) { + ZLOGI("new secret key"); + CryptoUtils::GetRandomKey(32, secretKey); // 32 is key length + KvStoreMetaManager::GetInstance().WriteSecretKeyToMeta(metaSecretKey, secretKey); + KvStoreMetaManager::GetInstance().WriteSecretKeyToFile(secretKeyFile, secretKey); + } else { + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGW("get secret key from meta failed, try to recover"); + KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + } + if (secretKey.empty()) { + ZLOGW("recover failed"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + } else { + if (alreadyCreated == Status::SUCCESS || FileExists(secretKeyFile)) { + ZLOGW("try to get an encrypted store with false option encrypt parameter"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + auto result = deviceAccountMap_.emplace(std::piecewise_construct, + std::forward_as_tuple(deviceAccountId), std::forward_as_tuple(deviceAccountId)); + if (!result.second) { + ZLOGE("emplace failed."); + FaultMsg msg = {FaultType::RUNTIME_FAULT, "user", __FUNCTION__, Fault::RF_GET_DB}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + callback(nullptr); + return Status::ERROR; + } + it = result.first; + } + Status statusTmp = (it->second).GetKvStore( + options, bundleName, storeIdTmp, secretKey, + [&](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }); + + ZLOGD("get kvstore return status:%d, deviceAccountId:[%s], bundleName:[%s].", + statusTmp, KvStoreUtils::ToBeAnonymous(deviceAccountId).c_str(), bundleName.c_str()); + if (statusTmp == Status::SUCCESS) { + struct KvStoreMetaData metaData { + .appId = trueAppId, + .appType = "harmony", + .bundleName = bundleName, + .dataDir = "default", + .deviceAccountId = deviceAccountId, + .deviceId = DeviceKvStoreImpl::GetLocalDeviceId(), + .isAutoSync = options.autoSync, + .isBackup = options.backup, + .isEncrypt = options.encrypt, + .kvStoreType = options.kvStoreType, + .schema = options.schema, + .storeId = storeIdTmp, + .userId = Constant::DEFAULT_GROUP_ID, + .uid = IPCSkeleton::GetCallingUid(), + .version = KVSTORE_META_VERSION, + .securityLevel = SecurityLevel::NO_LABEL, + }; + std::string jsonStr = metaData.Marshal(); + std::vector jsonVec(jsonStr.begin(), jsonStr.end()); + + return KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, UPDATE, jsonVec); + } + Status getKvStoreStatus = statusTmp; + ZLOGW("getKvStore failed with status %d", static_cast(getKvStoreStatus)); + if (getKvStoreStatus == Status::CRYPT_ERROR && options.encrypt) { + if (alreadyCreated != Status::SUCCESS) { + // create encrypted store failed, remove secret key + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + return Status::ERROR; + } + // get existing encrypted store failed, retry with key stored in file + Status status = KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + if (status != Status::SUCCESS) { + callback(nullptr); + return Status::CRYPT_ERROR; + } + // here callback is called twice + statusTmp = (it->second).GetKvStore( + options, bundleName, storeIdTmp, secretKey, + [&](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }); + } + + // if kvstore damaged and no backup file, then return DB_ERROR + if (statusTmp != Status::SUCCESS && getKvStoreStatus == Status::CRYPT_ERROR) { + // if backup file not exist, dont need recover + if (!CheckBackupFileExist(deviceAccountId, bundleName, storeId.storeId, options.securityLevel)) { + return Status::CRYPT_ERROR; + } + // remove damaged database + if (DeleteKvStoreOnly(storeIdTmp, deviceAccountId, bundleName) != Status::SUCCESS) { + ZLOGE("DeleteKvStoreOnly failed."); + return Status::DB_ERROR; + } + // recover database + return RecoverMultiKvStore(options, deviceAccountId, bundleName, storeId, secretKey, callback); + } + + return statusTmp; +} + +Status KvStoreDataService::GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGI("begin."); + if (callback == nullptr) { + ZLOGW("callback is nullptr"); + return Status::ERROR; + } + if (appId.appId.empty() || storeId.storeId.empty()) { + ZLOGW("appid or storeid empty"); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KvStoreType kvStoreType = options.kvStoreType; + if (kvStoreType != KvStoreType::DEVICE_COLLABORATION && kvStoreType != KvStoreType::SINGLE_VERSION) { + ZLOGE("invalid kvStore type."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + callback(nullptr); + ZLOGW("appId empty(permission issues?)"); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + callback(nullptr); + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + if (!CheckOptions(options, metaKey)) { + callback(nullptr); + ZLOGE("encrypt type or kvStore type is not the same"); + return Status::INVALID_ARGUMENT; + } + std::vector secretKey; + std::unique_ptr, void (*)(std::vector *)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + + bool outdated = false; + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp, + "SINGLE_KEY"); + auto secretKeyFile = KvStoreMetaManager::GetSecretSingleKeyFile(deviceAccountId, bundleName, storeIdTmp); + Status alreadyCreated = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaSecretKey, CHECK_EXIST_LOCAL); + if (options.encrypt) { + ZLOGI("Getting secret key"); + if (alreadyCreated != Status::SUCCESS) { + ZLOGI("new secret key"); + CryptoUtils::GetRandomKey(32, secretKey); // 32 is key length + KvStoreMetaManager::GetInstance().WriteSecretKeyToMeta(metaSecretKey, secretKey); + KvStoreMetaManager::GetInstance().WriteSecretKeyToFile(secretKeyFile, secretKey); + } else { + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGW("get secret key from meta failed, try to recover"); + KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + } + if (secretKey.empty()) { + ZLOGW("recover failed"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + } else { + if (alreadyCreated == Status::SUCCESS || FileExists(secretKeyFile)) { + ZLOGW("try to get an encrypted store with false option encrypt parameter"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + auto result = deviceAccountMap_.emplace(std::piecewise_construct, + std::forward_as_tuple(deviceAccountId), std::forward_as_tuple(deviceAccountId)); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + return Status::ERROR; + } + it = result.first; + } + auto newCallback = [&callback, outdated, deviceAccountId, bundleName, storeIdTmp](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }; + Status statusTmp = (it->second).GetSingleKvStore(options, bundleName, storeIdTmp, secretKey, newCallback); + if (statusTmp == Status::SUCCESS) { + KvStoreMetaData metaData { + .appId = trueAppId, + .appType = "harmony", + .bundleName = bundleName, + .dataDir = (it->second).GetDbDir(bundleName, options), + .deviceAccountId = deviceAccountId, + .deviceId = DeviceKvStoreImpl::GetLocalDeviceId(), + .isAutoSync = options.autoSync, + .isBackup = options.backup, + .isEncrypt = options.encrypt, + .kvStoreType = options.kvStoreType, + .schema = options.schema, + .storeId = storeIdTmp, + .userId = Constant::DEFAULT_GROUP_ID, + .uid = IPCSkeleton::GetCallingUid(), + .version = KVSTORE_META_VERSION, + .securityLevel = SecurityLevel::NO_LABEL, + }; + std::string jsonStr = metaData.Marshal(); + std::vector jsonVec(jsonStr.begin(), jsonStr.end()); + + return KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, UPDATE, jsonVec); + } + ZLOGW("getKvStore failed with status %d", static_cast(statusTmp)); + Status getKvStoreStatus = statusTmp; + if (getKvStoreStatus == Status::CRYPT_ERROR && options.encrypt) { + if (alreadyCreated != Status::SUCCESS) { + // create encrypted store failed, remove secret key + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + return Status::ERROR; + } + // get existing encrypted store failed, retry with key stored in file + Status status = KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + if (status != Status::SUCCESS) { + callback(nullptr); + return Status::CRYPT_ERROR; + } + // here callback is called twice + statusTmp = (it->second).GetSingleKvStore(options, bundleName, storeIdTmp, secretKey, newCallback); + } + + // if kvstore damaged and no backup file, then return DB_ERROR + if (statusTmp != Status::SUCCESS && getKvStoreStatus == Status::CRYPT_ERROR) { + // if backup file not exist, dont need recover + if (!CheckBackupFileExist(deviceAccountId, bundleName, storeId.storeId, options.securityLevel)) { + return Status::CRYPT_ERROR; + } + // remove damaged database + if (DeleteKvStoreOnly(storeIdTmp, deviceAccountId, bundleName) != Status::SUCCESS) { + ZLOGE("DeleteKvStoreOnly failed."); + return Status::DB_ERROR; + } + // recover database + return RecoverSingleKvStore(options, deviceAccountId, bundleName, storeId, secretKey, callback); + } + return statusTmp; +} + +bool KvStoreDataService::CheckOptions(const Options &options, const std::vector &metaKey) const +{ + ZLOGI("begin."); + KvStoreMetaData metaData; + metaData.version = 0; + Status statusTmp = KvStoreMetaManager::GetInstance().GetKvStoreMeta(metaKey, metaData); + if (statusTmp == Status::KEY_NOT_FOUND) { + ZLOGI("get metaKey not found."); + return true; + } + if (statusTmp != Status::SUCCESS) { + ZLOGE("get metaKey failed."); + return false; + } + ZLOGI("metaData encrypt is %d, kvStore type is %d, options encrypt is %d, kvStore type is %d", + static_cast(metaData.isEncrypt), static_cast(metaData.kvStoreType), + static_cast(options.encrypt), static_cast(options.kvStoreType)); + if (options.encrypt != metaData.isEncrypt) { + ZLOGE("checkOptions encrypt type is not the same."); + return false; + } + + if (options.kvStoreType != metaData.kvStoreType && metaData.version != 0) { + ZLOGE("checkOptions kvStoreType is not the same."); + return false; + } + ZLOGI("end."); + return true; +} + +bool KvStoreDataService::CheckBackupFileExist(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId, int securityLevel) +{ + auto pathType = KvStoreAppManager::ConvertPathType(bundleName, securityLevel); + auto backupFileName = Constant::Concatenate({ Constant::DEFAULT_GROUP_ID, "_", bundleName, "_", storeId }); + auto backFilePath = Constant::Concatenate({ BackupHandler::GetBackupPath(deviceAccountId, pathType), + "/", BackupHandler::GetHashedBackupName(backupFileName) }); + if (!BackupHandler::FileExists(backFilePath)) { + ZLOGE("BackupHandler file is not exist."); + return false; + } + return true; +} + +Status KvStoreDataService::RecoverSingleKvStore(const Options &options, + const std::string &deviceAccountId, + const std::string &bundleName, + const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback) +{ + // restore database + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + Options optionsTmp = options; + optionsTmp.createIfMissing = true; + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + ZLOGD("deviceAccountId not found"); + return Status::INVALID_ARGUMENT; + } + + sptr kvStorePtr; + Status statusTmp = (it->second).GetSingleKvStore( + optionsTmp, bundleName, storeIdTmp, secretKey, + [&kvStorePtr](sptr store) { kvStorePtr = store; }); + // restore database failed + if (statusTmp != Status::SUCCESS || kvStorePtr == nullptr) { + ZLOGE("RecoverSingleKvStore reget GetSingleKvStore failed."); + return Status::DB_ERROR; + } + // recover database from backup file + auto kvStorePtrTmp = static_cast(kvStorePtr.GetRefPtr()); + bool importRet = kvStorePtrTmp->Import(bundleName); + callback(kvStorePtr); + if (!importRet) { + ZLOGE("RecoverSingleKvStore Import failed."); + return Status::RECOVER_FAILED; + } + ZLOGD("RecoverSingleKvStore Import success."); + return Status::RECOVER_SUCCESS; +} + +Status KvStoreDataService::RecoverMultiKvStore(const Options &options, + const std::string &deviceAccountId, + const std::string &bundleName, + const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback) +{ + // restore database + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + Options optionsTmp = options; + optionsTmp.createIfMissing = true; + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + ZLOGD("deviceAccountId not found"); + return Status::INVALID_ARGUMENT; + } + + sptr kvStorePtr; + Status statusTmp = (it->second).GetKvStore( + optionsTmp, bundleName, storeIdTmp, secretKey, + [&kvStorePtr](sptr store) { + kvStorePtr = store; + }); + // restore database failed + if (statusTmp != Status::SUCCESS || kvStorePtr == nullptr) { + ZLOGE("RecoverMultiKvStore reget GetSingleKvStore failed."); + return Status::DB_ERROR; + } + + // recover database from backup file + auto kvStorePtrTmp = static_cast(kvStorePtr.GetRefPtr()); + if (!kvStorePtrTmp->Import(bundleName)) { + ZLOGE("RecoverMultiKvStore Import failed."); + return Status::RECOVER_FAILED; + } + ZLOGD("RecoverMultiKvStore Import success."); + callback(kvStorePtr); + return Status::RECOVER_SUCCESS; +} + +void KvStoreDataService::GetAllKvStoreId( + const AppId &appId, std::function &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGI("GetAllKvStoreId begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::vector storeIdList; + if (bundleName.empty() || bundleName.size() > MAX_APP_ID_LENGTH) { + ZLOGE("invalid appId."); + callback(Status::INVALID_ARGUMENT, storeIdList); + return; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + callback(Status::PERMISSION_DENIED, storeIdList); + return; + } + + auto &metaKvStoreDelegate = KvStoreMetaManager::GetInstance().GetMetaKvStore(); + if (metaKvStoreDelegate == nullptr) { + ZLOGE("metaKvStoreDelegate is null"); + callback(Status::DB_ERROR, storeIdList); + return; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return; + } + std::vector dbEntries; + DistributedDB::DBStatus dbStatus; + DistributedDB::Key dbKey = KvStoreMetaRow::GetKeyFor( + DeviceKvStoreImpl::GetLocalDeviceId() + Constant::KEY_SEPARATOR + + deviceAccountId + Constant::KEY_SEPARATOR + + "default" + Constant::KEY_SEPARATOR + + bundleName + Constant::KEY_SEPARATOR); + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = metaKvStoreDelegate->GetEntries(dbKey, dbEntries); + } + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("GetEntries delegate return error: %d.", static_cast(dbStatus)); + if (dbEntries.empty()) { + callback(Status::SUCCESS, storeIdList); + } else { + callback(Status::DB_ERROR, storeIdList); + } + return; + } + + for (const auto &entry : dbEntries) { + std::string keyStr = std::string(entry.key.begin(), entry.key.end()); + size_t pos = keyStr.find_last_of(Constant::KEY_SEPARATOR); + if (pos == std::string::npos) { + continue; + } + StoreId storeId; + storeId.storeId = keyStr.substr(pos + 1); + storeIdList.push_back(storeId); + } + callback(Status::SUCCESS, storeIdList); +} + +Status KvStoreDataService::CloseKvStore(const AppId &appId, const StoreId &storeId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGI("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + Status status = (it->second).CloseKvStore(bundleName, storeIdTmp); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + } + FaultMsg msg = {FaultType::RUNTIME_FAULT, "user", __FUNCTION__, Fault::RF_CLOSE_DB}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + ZLOGE("return STORE_NOT_OPEN."); + return Status::STORE_NOT_OPEN; +} + +/* close all opened kvstore */ +Status KvStoreDataService::CloseAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGD("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + return (it->second).CloseAllKvStore(bundleName); + } + ZLOGE("store not open."); + return Status::STORE_NOT_OPEN; +} + +Status KvStoreDataService::DeleteKvStore(const AppId &appId, const StoreId &storeId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + std::string bundleName = appId.appId; + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + // delete the backup file + auto backupFileName = Constant::Concatenate({ + AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), "_", bundleName, "_", storeId.storeId + }); + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + + auto backFilePath = Constant::Concatenate({ + BackupHandler::GetBackupPath(deviceAccountId, KvStoreAppManager::PATH_DE), "/", + BackupHandler::GetHashedBackupName(backupFileName) + }); + if (!BackupHandler::RemoveFile(backFilePath)) { + ZLOGE("DeleteKvStore RemoveFile backFilePath failed."); + } + backFilePath = Constant::Concatenate({ + BackupHandler::GetBackupPath(deviceAccountId, KvStoreAppManager::PATH_CE), "/", + BackupHandler::GetHashedBackupName(backupFileName) + }); + if (!BackupHandler::RemoveFile(backFilePath)) { + ZLOGE("DeleteKvStore RemoveFile backFilePath failed."); + } + return DeleteKvStore(appId, storeId, bundleName); +} + +/* delete all kv store */ +Status KvStoreDataService::DeleteAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("%s", appId.appId.c_str()); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + if (KvStoreUtils::GetAppIdByBundleName(bundleName).empty()) { + ZLOGE("invalid appId."); + return Status::PERMISSION_DENIED; + } + Status statusTmp; + std::vector existStoreIds; + GetAllKvStoreId(appId, [&statusTmp, &existStoreIds](Status status, std::vector &storeIds) { + statusTmp = status; + existStoreIds = std::move(storeIds); + }); + + if (statusTmp != Status::SUCCESS) { + ZLOGE("%s, error: %d ", bundleName.c_str(), static_cast(statusTmp)); + return statusTmp; + } + + for (const auto &storeId : existStoreIds) { + statusTmp = DeleteKvStore(appId, storeId); + if (statusTmp != Status::SUCCESS) { + ZLOGE("%s, error: %d ", bundleName.c_str(), static_cast(statusTmp)); + return statusTmp; + } + } + + return statusTmp; +} + +/* RegisterClientDeathObserver */ +Status KvStoreDataService::RegisterClientDeathObserver(const AppId &appId, sptr observer) +{ + ZLOGD("begin."); + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + return Status::PERMISSION_DENIED; + } + std::lock_guard lg(clientDeathObserverMutex_); + auto it = clientDeathObserverMap_.emplace(std::piecewise_construct, std::forward_as_tuple(bundleName), + std::forward_as_tuple(appId, *this, std::move(observer))); + ZLOGI("map size: %zu.", clientDeathObserverMap_.size()); + if (!it.second) { + ZLOGI("insert failed"); + return Status::ERROR; + } + ZLOGI("insert success"); + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + KvStoreTuple kvStoreTuple {userId, trueAppId}; + AppThreadInfo appThreadInfo {IPCSkeleton::GetCallingPid(), IPCSkeleton::GetCallingUid()}; + PermissionValidator::RegisterPermissionChanged(kvStoreTuple, appThreadInfo); + return Status::SUCCESS; +} + +Status KvStoreDataService::AppExit(const AppId &appId) +{ + ZLOGI("AppExit"); + // memory of parameter appId locates in a member of clientDeathObserverMap_ and will be freed after + // clientDeathObserverMap_ erase, so we have to take a copy if we want to use this parameter after erase operation. + AppId appIdTmp = appId; + { + std::lock_guard lg(clientDeathObserverMutex_); + clientDeathObserverMap_.erase(appIdTmp.appId); + ZLOGI("map size: %zu.", clientDeathObserverMap_.size()); + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(appIdTmp.appId); + if (trueAppId.empty()) { + ZLOGE("get appid for KvStore failed because of permission denied."); + return Status::PERMISSION_DENIED; + } + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(appIdTmp.appId); + KvStoreTuple kvStoreTuple {userId, trueAppId}; + PermissionValidator::UnregisterPermissionChanged(kvStoreTuple); + + CloseAllKvStore(appIdTmp); + return Status::SUCCESS; +} + +void KvStoreDataService::OnDump() +{ + ZLOGD("begin."); +} + +int KvStoreDataService::Dump(int fd, const std::vector &args) +{ + int uid = static_cast(IPCSkeleton::GetCallingUid()); + const int maxUid = 10000; + if (uid > maxUid) { + return 0; + } + dprintf(fd, "------------------------------------------------------------------\n"); + dprintf(fd, "DeviceAccount count : %u\n", static_cast(deviceAccountMap_.size())); + for (const auto &pair : deviceAccountMap_) { + dprintf(fd, "DeviceAccountID : %s\n", KvStoreUtils::GetAppIdByBundleName(pair.first).c_str()); + pair.second.Dump(fd); + } + return 0; +} + +void KvStoreDataService::OnStart() +{ + ZLOGI("distributeddata service onStart"); + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr != nullptr) { + ZLOGI("samgr exist."); + auto remote = samgr->CheckSystemAbility(DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID); + auto kvDataServiceProxy = iface_cast(remote); + if (kvDataServiceProxy != nullptr) { + ZLOGI("service has been registered."); + return; + } + } + + // register this to ServiceManager. + bool ret = SystemAbility::Publish(this); + if (!ret) { + FaultMsg msg = {FaultType::SERVICE_FAULT, "service", __FUNCTION__, Fault::SF_SERVICE_PUBLISH}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + } + Uninstaller::GetInstance().Init(this); + + // Initialize meta db delegate manager. + KvStoreMetaManager::GetInstance().InitMetaListener([this](const KvStoreMetaData &metaData) { + if (!metaData.isDirty) { + return; + } + + CloseKvStore({metaData.bundleName}, {metaData.storeId}); + DeleteKvStore({metaData.bundleName}, {metaData.storeId}); + }); + + // subscribe account event listener to EventNotificationMgr + AccountDelegate::GetInstance()->SubscribeAccountEvent(); + auto permissionCheckCallback = + [this](const std::string &userId, const std::string &appId, const std::string + &storeId, const std::string &deviceId, uint8_t flag) -> bool { + // temp add permission whilelist for ddmp; this should be config in ddmp manifest. + ZLOGD("checking sync permission start appid:%s, stid:%s.", appId.c_str(), storeId.c_str()); + return CheckPermissions(userId, appId, storeId, deviceId, flag); + }; + auto dbStatus = DistributedDB::KvStoreDelegateManager::SetPermissionCheckCallback(permissionCheckCallback); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("SetPermissionCheck callback failed."); + } + ZLOGI("autoLaunchRequestCallback start"); + auto autoLaunchRequestCallback = + [this](const std::string &identifier, DistributedDB::AutoLaunchParam ¶m) -> bool { + ResolveAutoLaunchParamByIdentifier(identifier, param); + return true; + }; + DistributedDB::KvStoreDelegateManager::SetAutoLaunchRequestCallback(autoLaunchRequestCallback); + + backup_ = std::make_unique(this); + std::string backupPath = BackupHandler::GetBackupPath(AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, + KvStoreAppManager::PATH_CE); + ZLOGI("backupPath is : %s ", backupPath.c_str()); + if (!ForceCreateDirectory(backupPath)) { + ZLOGE("backup create directory failed."); + } + backup_->BackSchedule(); + std::thread th = std::thread([]() { + sleep(TEN_SEC); + KvStoreAppAccessor::GetInstance().EnableKvStoreAutoLaunch(); + }); + th.detach(); + ZLOGI("Publish ret: %d", static_cast(ret)); +} + +void KvStoreDataService::ResolveAutoLaunchParamByIdentifier(const std::string &identifier, + DistributedDB::AutoLaunchParam ¶m) +{ + ZLOGI("start"); + std::map entries; + if (KvStoreMetaManager::GetInstance().GetFullMetaData(entries)) { + for (const auto &entry : entries) { + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId( + entry.second.kvStoreMetaData.bundleName); + const std::string &curIdentifier = DistributedDB::KvStoreDelegateManager::GetKvStoreIdentifier(userId, + entry.second.kvStoreMetaData.appId, entry.second.kvStoreMetaData.storeId); + if (identifier == curIdentifier) { + ZLOGI("identifier find"); + DistributedDB::AutoLaunchOption option; + option.createIfNecessary = false; + option.isEncryptedDb = entry.second.kvStoreMetaData.isEncrypt; + DistributedDB::CipherPassword password; + const std::vector &secretKey = entry.second.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Get secret key failed."); + } + option.passwd = password; + option.schema = entry.second.kvStoreMetaData.schema; + option.createDirByStoreIdOnly = true; + option.dataDir = entry.second.kvStoreMetaData.dataDir; + option.secOption = KvStoreAppManager::ConvertSecurity(entry.second.kvStoreMetaData.securityLevel); + param.userId = userId; + param.appId = entry.second.kvStoreMetaData.appId; + param.storeId = entry.second.kvStoreMetaData.storeId; + param.option = option; + } + } + } +} + +bool KvStoreDataService::CheckPermissions(const std::string &userId, const std::string &appId, + const std::string &storeId, const std::string &deviceId, uint8_t flag) const +{ + auto &instance = KvStoreMetaManager::GetInstance(); + KvStoreMetaData metaData; + auto localDevId = DeviceKvStoreImpl::GetLocalDeviceId(); + auto qstatus = instance.QueryKvStoreMetaDataByDeviceIdAndAppId(localDevId, appId, metaData); + if (qstatus != Status::SUCCESS) { + qstatus = instance.QueryKvStoreMetaDataByDeviceIdAndAppId("", appId, metaData); // local device id maybe null + if (qstatus != Status::SUCCESS) { + ZLOGW("query appId failed."); + return false; + } + } + if (metaData.appType.compare("default") == 0) { + ZLOGD("default, dont check sync permission."); + return true; + } + Status status = instance.CheckSyncPermission(userId, appId, storeId, flag, deviceId); + if (status != Status::SUCCESS) { + ZLOGW("PermissionCheck failed."); + return false; + } + + if (metaData.appType.compare("harmony") != 0) { + ZLOGD("it's A app, dont check sync permission."); + return true; + } + + if (PermissionValidator::IsAutoLaunchEnabled(appId)) { + return true; + } + bool ret = PermissionValidator::CheckSyncPermission(userId, appId, metaData.uid); + ZLOGD("checking sync permission ret:%d.", ret); + return ret; +} + +void KvStoreDataService::OnStop() +{ + ZLOGI("begin."); + if (backup_ != nullptr) { + backup_.reset(); + backup_ = nullptr; + } +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreClientDeathObserverImpl( + const AppId &appId, KvStoreDataService &service, sptr observer) + : appId_(appId), dataService_(service), observerProxy_(std::move(observer)), + deathRecipient_(new KvStoreDeathRecipient(*this)) +{ + ZLOGI("KvStoreClientDeathObserverImpl"); + + if (observerProxy_ != nullptr) { + ZLOGI("add death recipient"); + observerProxy_->AddDeathRecipient(deathRecipient_); + } else { + ZLOGW("observerProxy_ is nullptr"); + } +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::~KvStoreClientDeathObserverImpl() +{ + ZLOGI("~KvStoreClientDeathObserverImpl"); + if (deathRecipient_ != nullptr && observerProxy_ != nullptr) { + ZLOGI("remove death recipient"); + observerProxy_->RemoveDeathRecipient(deathRecipient_); + } +} + +void KvStoreDataService::KvStoreClientDeathObserverImpl::NotifyClientDie() +{ + ZLOGI("appId: %s", appId_.appId.c_str()); + dataService_.AppExit(appId_); +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::KvStoreDeathRecipient( + KvStoreClientDeathObserverImpl &kvStoreClientDeathObserverImpl) + : kvStoreClientDeathObserverImpl_(kvStoreClientDeathObserverImpl) +{ + ZLOGI("KvStore Client Death Observer"); +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::~KvStoreDeathRecipient() +{ + ZLOGI("KvStore Client Death Observer"); +} + +void KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::OnRemoteDied( + const wptr &remote) +{ + ZLOGI("begin"); + kvStoreClientDeathObserverImpl_.NotifyClientDie(); +} + +Status KvStoreDataService::DeleteKvStore(const AppId &appId, const StoreId &storeId, const std::string &trueAppId) +{ + ZLOGI("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + return Status::INVALID_ARGUMENT; + } + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + Status status; + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + status = (it->second).DeleteKvStore(trueAppId, storeIdTmp); + } else { + KvStoreUserManager kvStoreUserManager(deviceAccountId); + status = kvStoreUserManager.DeleteKvStore(trueAppId, storeIdTmp); + } + + if (status == Status::SUCCESS) { + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + status = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, DELETE); + if (status != Status::SUCCESS) { + ZLOGW("Remove Kvstore Metakey failed."); + } + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + KvStoreMetaManager::GetInstance().DeleteStrategyMeta(bundleName, storeIdTmp); + } + return status; +} + + +Status KvStoreDataService::DeleteKvStoreOnly(const std::string &storeIdTmp, const std::string &deviceAccountId, + const std::string &bundleName) +{ + ZLOGI("DeleteKvStoreOnly begin."); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + return (it->second).DeleteKvStore(bundleName, storeIdTmp); + } + KvStoreUserManager kvStoreUserManager(deviceAccountId); + return kvStoreUserManager.DeleteKvStore(bundleName, storeIdTmp); +} + +void KvStoreDataService::AccountEventChanged(const AccountEventInfo &eventInfo) +{ + ZLOGI("account event %d changed process, begin.", eventInfo.status); + std::lock_guard lg(accountMutex_); + switch (eventInfo.status) { + case AccountStatus::HARMONY_ACCOUNT_LOGIN: + case AccountStatus::HARMONY_ACCOUNT_LOGOUT: { + g_kvStoreAccountEventStatus = 1; + // migrate all kvstore belong to this device account + for (auto &it : deviceAccountMap_) { + (it.second).MigrateAllKvStore(eventInfo.harmonyAccountId); + } + g_kvStoreAccountEventStatus = 0; + break; + } + case AccountStatus::DEVICE_ACCOUNT_DELETE: { + g_kvStoreAccountEventStatus = 1; + // delete all kvstore belong to this device account + for (auto &it : deviceAccountMap_) { + (it.second).DeleteAllKvStore(); + } + auto it = deviceAccountMap_.find(eventInfo.deviceAccountId); + if (it != deviceAccountMap_.end()) { + deviceAccountMap_.erase(eventInfo.deviceAccountId); + } + std::string deviceAccountKvStoreDataDir = + Constant::Concatenate({Constant::ROOT_PATH_DE, "/", Constant::SERVICE_NAME, + "/", eventInfo.deviceAccountId}); + ForceRemoveDirectory(deviceAccountKvStoreDataDir); + deviceAccountKvStoreDataDir = + Constant::Concatenate({Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, + "/", eventInfo.deviceAccountId}); + ForceRemoveDirectory(deviceAccountKvStoreDataDir); + g_kvStoreAccountEventStatus = 0; + break; + } + default: { + break; + } + } + ZLOGI("account event %d changed process, end.", eventInfo.status); +} + +Status KvStoreDataService::GetLocalDevice(DeviceInfo &device) +{ + auto tmpDevice = KvStoreUtils::GetProviderInstance().GetLocalBasicInfo(); + device = {tmpDevice.deviceId, tmpDevice.deviceName, tmpDevice.deviceType}; + return Status::SUCCESS; +} + +Status KvStoreDataService::GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) +{ + auto devices = KvStoreUtils::GetProviderInstance().GetRemoteNodesBasicInfo(); + for(auto const &device : devices) { + deviceInfoList.push_back({device.deviceId, device.deviceName, device.deviceType}); + } + ZLOGD("strategy is %d.", strategy); + return Status::SUCCESS; +} + +Status KvStoreDataService::StartWatchDeviceChange(sptr observer, + DeviceFilterStrategy strategy) +{ + if (observer == nullptr) { + ZLOGD("observer is null"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(deviceListenerMutex_); + if (deviceListener_ == nullptr) { + deviceListener_ = std::make_shared(deviceListeners_); + KvStoreUtils::GetProviderInstance().StartWatchDeviceChange(deviceListener_.get(), {"serviceWatcher"}); + } + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + deviceListeners_.insert({objectPtr, observer}); + ZLOGD("strategy is %d.", strategy); + return Status::SUCCESS; +} + +Status KvStoreDataService::StopWatchDeviceChange(sptr observer) +{ + if (observer == nullptr) { + ZLOGD("observer is null"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(deviceListenerMutex_); + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + auto it = deviceListeners_.find(objectPtr); + if (it == deviceListeners_.end()) { + return Status::ILLEGAL_STATE; + } + deviceListeners_.erase(it->first); + return Status::SUCCESS; +} + +bool DbMetaCallbackDelegateMgr::GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) +{ + if (IsDestruct()) { + return false; + } + DistributedDB::DBStatus ret = delegate_->GetKvStoreDiskSize(storeId, size); + return (ret == DistributedDB::DBStatus::OK); +} + +void DbMetaCallbackDelegateMgr::GetKvStoreKeys(std::vector &dbStats) +{ + if (IsDestruct()) { + return; + } + DistributedDB::DBStatus dbStatusTmp; + Option option {.createIfNecessary = true, .isMemoryDb = false, .isEncryptedDb = false}; + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegatePtr = nullptr; + delegate_->GetKvStore( + Constant::SERVICE_META_DB_NAME, option, + [&kvStoreNbDelegatePtr, &dbStatusTmp](DistributedDB::DBStatus dbStatus, + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) { + kvStoreNbDelegatePtr = kvStoreNbDelegate; + dbStatusTmp = dbStatus; + }); + + if (dbStatusTmp != DistributedDB::DBStatus::OK) { + return; + } + DistributedDB::Key dbKey = KvStoreMetaRow::GetKeyFor(""); + std::vector entries; + kvStoreNbDelegatePtr->GetEntries(dbKey, entries); + if (entries.empty()) { + delegate_->CloseKvStore(kvStoreNbDelegatePtr); + return; + } + for (auto const &entry : entries) { + std::string key = std::string(entry.key.begin(), entry.key.end()); + std::vector out; + Split(key, Constant::KEY_SEPARATOR, out); + if (out.size() >= VECTOR_SIZE) { + StoreInfo storeInfo = {out[USER_ID], out[APP_ID], out[STORE_ID]}; + dbStats.push_back(std::move(storeInfo)); + } + } + delegate_->CloseKvStore(kvStoreNbDelegatePtr); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_data_service.h b/services/distributeddataservice/app/src/kvstore_data_service.h new file mode 100755 index 000000000..03eead02f --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_data_service.h @@ -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. + */ + +#ifndef KVSTORE_DATASERVICE_H +#define KVSTORE_DATASERVICE_H + +#include +#include +#include +#include "constant.h" +#include "ikvstore_data_service.h" +#include "kvstore_impl.h" +#include "kvstore_user_manager.h" +#include "single_kvstore_impl.h" +#include "system_ability.h" +#include "reporter.h" +#include "types.h" +#include "account_delegate.h" +#include "backup_handler.h" +#include "device_change_listener_impl.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreAccountObserver; +class KvStoreDataService : public SystemAbility, public KvStoreDataServiceStub { + DECLARE_SYSTEM_ABILITY(KvStoreDataService); + +public: + // record kvstore meta version for compatible, should update when modify kvstore meta structure. + static constexpr int KVSTORE_META_VERSION = 1; + + explicit KvStoreDataService(bool runOnCreate = false); + explicit KvStoreDataService(int32_t systemAbilityId, bool runOnCreate = false); + virtual ~KvStoreDataService(); + + Status GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) override; + + Status GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) override; + + void GetAllKvStoreId(const AppId &appId, std::function &)> callback) override; + + Status CloseKvStore(const AppId &appId, const StoreId &storeId) override; + + Status CloseAllKvStore(const AppId &appId) override; + + Status DeleteKvStore(const AppId &appId, const StoreId &storeId) override; + + Status DeleteAllKvStore(const AppId &appId) override; + + Status RegisterClientDeathObserver(const AppId &appId, sptr observer) override; + + Status GetLocalDevice(DeviceInfo &device) override; + Status GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) override; + Status StartWatchDeviceChange(sptr observer, DeviceFilterStrategy strategy) override; + Status StopWatchDeviceChange(sptr observer) override; + + void OnDump() override; + + int Dump(int fd, const std::vector &args) override; + + void OnStart() override; + + void OnStop() override; + + Status DeleteKvStore(const AppId &appId, const StoreId &storeId, const std::string &trueAppId); + + Status DeleteKvStoreOnly(const std::string &storeIdTmp, const std::string &deviceAccountId, + const std::string &bundleName); + + void AccountEventChanged(const AccountEventInfo &eventInfo); + + bool CheckBackupFileExist(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId, int securityLevel); + + Status RecoverSingleKvStore(const Options &options, const std::string &deviceAccountId, + const std::string &bundleName, const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback); + + Status RecoverMultiKvStore(const Options &options, const std::string &deviceAccountId, + const std::string &bundleName, const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback); +private: + class KvStoreClientDeathObserverImpl { + public: + KvStoreClientDeathObserverImpl(const AppId &appId, KvStoreDataService &service, sptr observer); + + virtual ~KvStoreClientDeathObserverImpl(); + + private: + class KvStoreDeathRecipient : public IRemoteObject::DeathRecipient { + public: + explicit KvStoreDeathRecipient(KvStoreClientDeathObserverImpl &kvStoreClientDeathObserverImpl); + virtual ~KvStoreDeathRecipient(); + void OnRemoteDied(const wptr &remote) override; + + private: + KvStoreClientDeathObserverImpl &kvStoreClientDeathObserverImpl_; + }; + void NotifyClientDie(); + AppId appId_; + KvStoreDataService &dataService_; + sptr observerProxy_; + sptr deathRecipient_; + }; + + void Initialize(); + + Status AppExit(const AppId &appId); + + bool CheckBundleName(const std::string &bundleName) const; + + bool CheckStoreId(const std::string &storeId) const; + + bool CheckPermissions(const std::string &userId, const std::string &appId, const std::string &storeId, + const std::string &deviceId, uint8_t flag) const; + void ResolveAutoLaunchParamByIdentifier(const std::string &identifier, DistributedDB::AutoLaunchParam ¶m); + + bool CheckOptions(const Options &options, const std::vector &metaKey) const; + + static constexpr int TEN_SEC = 10; + + std::mutex accountMutex_; + std::map deviceAccountMap_; + std::mutex clientDeathObserverMutex_; + std::map clientDeathObserverMap_; + std::shared_ptr accountEventObserver_; + std::unique_ptr backup_; + std::map> deviceListeners_; + std::mutex deviceListenerMutex_; + std::shared_ptr deviceListener_; +}; + +class DbMetaCallbackDelegateMgr : public DbMetaCallbackDelegate { +public: + using Option = DistributedDB::KvStoreNbDelegate::Option; + virtual ~DbMetaCallbackDelegateMgr() {} + + explicit DbMetaCallbackDelegateMgr(DistributedDB::KvStoreDelegateManager *delegate) + : delegate_(delegate) {} + bool GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) override; + void GetKvStoreKeys(std::vector &dbStats) override; + bool IsDestruct() + { + return delegate_ == nullptr; + } + +private: + void Split(const std::string &str, const std::string &delimiter, std::vector &out) + { + size_t start; + size_t end = 0; + while ((start = str.find_first_not_of(delimiter, end)) != std::string::npos) { + end = str.find(delimiter, start); + if (end == std::string::npos) { + end = str.size(); + } + out.push_back(str.substr(start, end - start)); + } + } + + DistributedDB::KvStoreDelegateManager *delegate_ {}; + static const inline int USER_ID = 0; + static const inline int APP_ID = 1; + static const inline int STORE_ID = 2; + static const inline int VECTOR_SIZE = 2; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_DATASERVICE_H diff --git a/services/distributeddataservice/app/src/kvstore_impl.cpp b/services/distributeddataservice/app/src/kvstore_impl.cpp new file mode 100755 index 000000000..6ea4f718d --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_impl.cpp @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreImpl" + +#include "kvstore_impl.h" +#include +#include +#include +#include "backup_handler.h" +#include "constant.h" +#include "dds_trace.h" +#include "kvstore_account_observer.h" +#include "kvstore_data_service.h" +#include "kvstore_meta_manager.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "permission_validator.h" +#include "reporter.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreImpl::KvStoreImpl(const Options &options, + const std::string &deviceAccountId, const std::string &bundleName, const std::string &storeId, + const std::string &appDirectory, DistributedDB::KvStoreDelegate *kvStoreDelegate) + : options_(options), + deviceAccountId_(deviceAccountId), + bundleName_(bundleName), + storeId_(storeId), + storePath_(Constant::Concatenate({ appDirectory, storeId })), + kvStoreDelegate_(kvStoreDelegate), + storeObserverMutex_(), + observerSet_(), + openCount_(1) +{ + ZLOGI("construct"); +} + +void KvStoreImpl::GetKvStoreSnapshot(sptr observer, + std::function)> callback) +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + callback(Status::DB_ERROR, nullptr); + return; + } + + DistributedDB::KvStoreSnapshotDelegate *retSnapshotKvStore = nullptr; + DistributedDB::DBStatus retSnapshotStatus; + auto snapshotCallbackFunction = [&](DistributedDB::DBStatus status, + DistributedDB::KvStoreSnapshotDelegate *snapshot) { + retSnapshotStatus = status; + retSnapshotKvStore = snapshot; + }; + std::lock_guard lg(storeSnapshotMutex_); + KvStoreObserverImpl *kvStoreObserverImpl = nullptr; + if (observer == nullptr) { + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreDelegate_->GetKvStoreSnapshot(nullptr, snapshotCallbackFunction); + } + } else { + kvStoreObserverImpl = + new (std::nothrow) KvStoreObserverImpl(SubscribeType::SUBSCRIBE_TYPE_ALL, observer); + if (kvStoreObserverImpl == nullptr) { + ZLOGW("new KvStoreObserverImpl failed"); + callback(Status::ERROR, nullptr); + return; + } + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreDelegate_->GetKvStoreSnapshot(kvStoreObserverImpl, snapshotCallbackFunction); + } + } + + if (retSnapshotStatus != DistributedDB::DBStatus::OK || retSnapshotKvStore == nullptr) { + ZLOGE("delegate return nullptr."); + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + if (retSnapshotStatus == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("GetKvStoreSnapshot failed, distributeddb need recover."); + IMPORT_DATABASE(bundleName_); + } else { + callback(Status::DB_ERROR, nullptr); + } + return; + } + + ZLOGD("get delegate"); + KvStoreSnapshotImpl *snapshot = new (std::nothrow) KvStoreSnapshotImpl(retSnapshotKvStore, kvStoreObserverImpl); + if (snapshot == nullptr) { + ZLOGW("new KvStoreSnapshotImpl failed"); + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + callback(Status::ERROR, nullptr); + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreDelegate_->ReleaseKvStoreSnapshot(retSnapshotKvStore); + } + return; + } + sptr kvStoreSnapshotImpl = snapshot; + callback(Status::SUCCESS, kvStoreSnapshotImpl); + snapshotMap_.emplace(snapshot, std::move(kvStoreSnapshotImpl)); + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); +} + +Status KvStoreImpl::ReleaseKvStoreSnapshot(sptr iKvStoreSnapshot) +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is nullptr."); + return Status::DB_ERROR; + } + + if (iKvStoreSnapshot == nullptr) { + ZLOGE("snapshot is nullptr."); + return Status::ERROR; + } + + std::lock_guard lg(storeSnapshotMutex_); + Status status = static_cast(iKvStoreSnapshot.GetRefPtr())->Release(kvStoreDelegate_); + if (status == Status::SUCCESS) { + auto it = snapshotMap_.find(static_cast(iKvStoreSnapshot.GetRefPtr())); + if (it != snapshotMap_.end()) { + snapshotMap_.erase(it); + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + } else { + FaultMsg msg = {FaultType::RUNTIME_FAULT, "user", __FUNCTION__, Fault::RF_RELEASE_SNAPSHOT}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + } + return status; +} + +Status KvStoreImpl::Put(const Key &key, const Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::vector keyData = Constant::TrimCopy>(key.Data()); + + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + DistributedDB::Key dbKey = keyData; + DistributedDB::Value dbValue = value.Data(); + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->Put(dbKey, dbValue); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Put failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate put failed."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::PutBatch(const std::vector &entries) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + // temporary transform. + std::vector dbEntries; + for (auto &entry : entries) { + DistributedDB::Entry dbEntry; + + std::vector keyData = Constant::TrimCopy>(entry.key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + dbEntry.key = keyData; + dbEntry.value = entry.value.Data(); + dbEntries.push_back(dbEntry); + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->PutBatch(dbEntries); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("PutBatch failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate PutBatch failed."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::Delete(const Key &key) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + DistributedDB::Key dbKey = keyData; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->Delete(dbKey); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Delete failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate Delete failed."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::DeleteBatch(const std::vector &keys) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + // temporary transform. + std::vector dbKeys; + for (auto &key : keys) { + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + DistributedDB::Key keyTmp = keyData; + dbKeys.push_back(keyTmp); + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->DeleteBatch(dbKeys); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("DeleteBatch failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate DeleteBatch failed."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::Clear() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->Clear(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Clear failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate Clear failed."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::StartTransaction() +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->StartTransaction(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("StartTransaction failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::Commit() +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->Commit(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Commit failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status KvStoreImpl::Rollback() +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreDelegate_->Rollback(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Rollback failed, distributeddb need recover."); + return IMPORT_DATABASE(bundleName_); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +InnerStatus KvStoreImpl::Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGW("start Close"); + if (openCount_ > 1) { + openCount_--; + return InnerStatus::DECREASE_REFCOUNT; + } + Status status = ForceClose(kvStoreDelegateManager); + if (status == Status::SUCCESS) { + return InnerStatus::SUCCESS; + } + return InnerStatus::ERROR; +} + +Status KvStoreImpl::ForceClose(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("start ForceClose, current openCount is %d.", openCount_); + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr || kvStoreDelegateManager == nullptr) { + ZLOGW("close got nullptr"); + return Status::INVALID_ARGUMENT; + } + ZLOGI("ForceClose start to clean observer"); + std::lock_guard observerSetLockGuard(storeObserverMutex_); + for (auto observer = observerSet_.begin(); observer != observerSet_.end();) { + DistributedDB::DBStatus dbStatus = kvStoreDelegate_->UnRegisterObserver(*observer); + if (dbStatus == DistributedDB::DBStatus::OK) { + delete *observer; + observer = observerSet_.erase(observer); + } else { + ZLOGW("Force close kvstore failed during UnRegisterObserver, status %d.", dbStatus); + return Status::ERROR; + } + } + ZLOGI("ForceClose start to clean snapshot"); + std::lock_guard snapshotMapLockGuard(storeSnapshotMutex_); + for (auto snapshotPair = snapshotMap_.begin(); snapshotPair != snapshotMap_.end();) { + auto *snapshotImpl = static_cast(snapshotPair->second.GetRefPtr()); + if (snapshotImpl != nullptr) { + auto status = snapshotImpl->Release(kvStoreDelegate_); + if (status != Status::SUCCESS) { + ZLOGW("Force close kvstore failed during release snapshot, errCode %d", status); + return status; + } + } + snapshotPair = snapshotMap_.erase(snapshotPair); + } + DistributedDB::DBStatus status = kvStoreDelegateManager->CloseKvStore(kvStoreDelegate_); + if (status == DistributedDB::DBStatus::OK) { + kvStoreDelegate_ = nullptr; + ZLOGI("end ForceClose."); + return Status::SUCCESS; + } + ZLOGI("ForceClose close failed with error code %d.", status); + return Status::ERROR; +} + +Status KvStoreImpl::MigrateKvStore(const std::string &harmonyAccountId, + const std::string &kvStoreDataDir, + DistributedDB::KvStoreDelegateManager *oldDelegateMgr, + DistributedDB::KvStoreDelegateManager *&newDelegateMgr) +{ + ZLOGI("begin."); + std::unique_lock lock(storeDelegateMutex_); + if (oldDelegateMgr == nullptr) { + ZLOGW("kvStore delegate manager is nullptr."); + return Status::INVALID_ARGUMENT; + } + + ZLOGI("create new KvStore."); + std::vector secretKey; // expected get secret key from meta kvstore successful when encrypt flag is true. + std::unique_ptr, void(*)(std::vector*)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId_, "default", bundleName_, storeId_, "KEY"); + if (options_.encrypt) { + bool outdated = false; // ignore outdated flag during rebuild kvstore. + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGE("Get secret key from meta kvstore failed."); + return Status::CRYPT_ERROR; + } + } + + DistributedDB::KvStoreDelegate::Option dbOption; + Status status = KvStoreAppManager::InitDbOption(options_, secretKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitDbOption failed."); + return status; + } + if (newDelegateMgr == nullptr) { + auto appId = KvStoreUtils::GetAppIdByBundleName(bundleName_); + if (appId.empty()) { + ZLOGE("Get appId by bundle name failed."); + return Status::MIGRATION_KVSTORE_FAILED; + } + newDelegateMgr = new (std::nothrow) DistributedDB::KvStoreDelegateManager(appId, harmonyAccountId); + if (newDelegateMgr == nullptr) { + ZLOGE("new KvStoreDelegateManager failed."); + return Status::MIGRATION_KVSTORE_FAILED; + } + DistributedDB::KvStoreConfig kvStoreConfig {kvStoreDataDir}; + newDelegateMgr->SetKvStoreConfig(kvStoreConfig); + } + + DistributedDB::DBStatus dbStatus = DistributedDB::DBStatus::OK; + DistributedDB::KvStoreDelegate *kvStoreDelegate = nullptr; // new KvStoreDelegate get from distributed DB. + newDelegateMgr->GetKvStore(storeId_, dbOption, + [&](DistributedDB::DBStatus result, DistributedDB::KvStoreDelegate *delegate) { + kvStoreDelegate = delegate; + dbStatus = result; + }); + + if (kvStoreDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr, dbStatusTmp: %d", static_cast(dbStatus)); + return Status::DB_ERROR; + } + + status = RebuildKvStoreObserver(kvStoreDelegate); + if (status != Status::SUCCESS) { + ZLOGI("rebuild KvStore observer failed, errCode %d.", static_cast(status)); + // skip this failed, continue to do other rebuild process. + } + + status = RebuildKvStoreSnapshot(kvStoreDelegate); + if (status != Status::SUCCESS) { + ZLOGI("rebuild KvStore snapshot failed, errCode %d.", static_cast(status)); + // skip this failed, continue to do close kvstore process. + } + + ZLOGI("close old KvStore."); + dbStatus = oldDelegateMgr->CloseKvStore(kvStoreDelegate_); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGI("rebuild KvStore failed during close KvStore, errCode %d.", static_cast(dbStatus)); + newDelegateMgr->CloseKvStore(kvStoreDelegate); + return Status::DB_ERROR; + } + + ZLOGI("update kvstore delegate."); + kvStoreDelegate_ = kvStoreDelegate; + return Status::SUCCESS; +} + +Status KvStoreImpl::RebuildKvStoreObserver(DistributedDB::KvStoreDelegate *kvStoreDelegate) +{ + ZLOGI("rebuild observer."); + if (kvStoreDelegate_ == nullptr || kvStoreDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + std::lock_guard observerSetLockGuard(storeObserverMutex_); + Status status = Status::SUCCESS; + DistributedDB::DBStatus dbStatus; + for (const auto observer : observerSet_) { + dbStatus = kvStoreDelegate_->UnRegisterObserver(observer); + if (dbStatus != DistributedDB::OK) { + status = Status::DB_ERROR; + ZLOGW("rebuild observer failed during UnRegisterObserver, status %d.", static_cast(dbStatus)); + continue; + } + + dbStatus = kvStoreDelegate->RegisterObserver(observer); + if (dbStatus != DistributedDB::OK) { + status = Status::DB_ERROR; + ZLOGW("rebuild observer failed during RegisterObserver, status %d.", static_cast(dbStatus)); + continue; + } + } + return status; +} + +Status KvStoreImpl::RebuildKvStoreSnapshot(DistributedDB::KvStoreDelegate *kvStoreDelegate) +{ + ZLOGI("rebuild snapshot."); + if (kvStoreDelegate_ == nullptr || kvStoreDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + std::lock_guard snapshotMapLockGuard(storeSnapshotMutex_); + Status retStatus = Status::SUCCESS; + for (const auto &snapshotPair : snapshotMap_) { + auto *snapshot = static_cast(snapshotPair.second.GetRefPtr()); + if (snapshot == nullptr) { + continue; + } + Status status = snapshot->Release(kvStoreDelegate_); + if (status != Status::SUCCESS) { + retStatus = status; + ZLOGW("rebuild snapshot failed during release snapshot, errCode %d", static_cast(status)); + continue; + } + + status = snapshot->MigrateKvStore(kvStoreDelegate); + if (status != Status::SUCCESS) { + retStatus = status; + ZLOGW("rebuild snapshot failed during migrate snapshot, errCode %d", static_cast(status)); + continue; + } + } + return retStatus; +} + +void KvStoreImpl::IncreaseOpenCount() +{ + openCount_++; +} + +/* subscribe kv store */ +Status KvStoreImpl::SubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + if (observer == nullptr) { + ZLOGE("observer is nullptr."); + return Status::INVALID_ARGUMENT; + } + + std::lock_guard lg(storeObserverMutex_); + KvStoreObserverImpl *kvStoreObserverImpl = new (std::nothrow) KvStoreObserverImpl(subscribeType, observer); + if (kvStoreObserverImpl == nullptr) { + ZLOGE("kvStoreObserverImpl is nullptr."); + return Status::ERROR; + } + + if (observerSet_.find(kvStoreObserverImpl) != observerSet_.end()) { + ZLOGI("already subscribed."); + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + return Status::STORE_ALREADY_SUBSCRIBE; + } + + DistributedDB::DBStatus status = kvStoreDelegate_->RegisterObserver(kvStoreObserverImpl); + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + return Status::DB_ERROR; + } + + auto it = observerSet_.insert(kvStoreObserverImpl); + ZLOGI("set size: %zu.", observerSet_.size()); + if (!(it.second)) { + status = kvStoreDelegate_->UnRegisterObserver(kvStoreObserverImpl); + ZLOGI("insert failed set size: %zu status: %d.", observerSet_.size(), static_cast(status)); + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + return Status::STORE_ALREADY_SUBSCRIBE; + } else { + ZLOGI("insert success set size: %zu.", observerSet_.size()); + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +/* unsubscribe kv store */ +Status KvStoreImpl::UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + Status status = Status::DB_ERROR; + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return status; + } + + if (observer == nullptr) { + ZLOGE("observer is nullptr."); + return Status::INVALID_ARGUMENT; + } + + std::lock_guard lg(storeObserverMutex_); + KvStoreObserverImpl *kvStoreObserverImpl = new (std::nothrow) KvStoreObserverImpl(subscribeType, observer); + if (kvStoreObserverImpl == nullptr) { + ZLOGE("kvStoreObserverImpl is nullptr."); + return Status::ERROR; + } + + ZLOGI("set size: %zu.", observerSet_.size()); + auto it = observerSet_.find(kvStoreObserverImpl); + if (it != observerSet_.end()) { + DistributedDB::DBStatus dbStatus; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = kvStoreDelegate_->UnRegisterObserver(*it); + } + if (dbStatus == DistributedDB::DBStatus::OK) { + delete *it; + observerSet_.erase(it); + status = Status::SUCCESS; + } + ZLOGE("delegate return status: %d.", static_cast(dbStatus)); + } else { + ZLOGW("No existing observer to unsubscribe. Return success."); + status = Status::SUCCESS; + } + + delete kvStoreObserverImpl; + kvStoreObserverImpl = nullptr; + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + ZLOGI("return status: %d", static_cast(status)); + return status; +} + +Status KvStoreImpl::ReKey(const std::vector &key) { + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin"); + std::shared_lock lock(storeDelegateMutex_); + if (kvStoreDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::CipherPassword password; + auto status = password.SetValue(key.data(), key.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus dbStatus; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = kvStoreDelegate_->Rekey(password); + } + if (dbStatus == DistributedDB::DBStatus::OK) { + ZLOGI("succeed"); + return Status::SUCCESS; + } + return Status::ERROR; +} + +const std::string KvStoreImpl::GetStorePath() +{ + return storePath_; +} + +KvStoreImpl::~KvStoreImpl() +{ + ZLOGI("destruct"); +} + +bool KvStoreImpl::Import(const std::string &bundleName) const +{ + ZLOGI("KvStoreImpl Import start"); + const std::string harmonyAccountId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId_, harmonyAccountId, bundleName, storeId_, + "SINGLE_KEY"); + std::vector secretKey; + bool outdated = false; + auto trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + + MetaData metaData{0}; + metaData.kvStoreMetaData.deviceAccountId = deviceAccountId_; + metaData.kvStoreMetaData.userId = harmonyAccountId; + metaData.kvStoreMetaData.bundleName = bundleName; + metaData.kvStoreMetaData.appId = trueAppId; + metaData.kvStoreMetaData.storeId = storeId_; + metaData.secretKeyMetaData.secretKey = secretKey; + std::shared_lock lock(storeDelegateMutex_); + return std::make_unique()->MultiKvStoreRecover(metaData, kvStoreDelegate_); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_impl.h b/services/distributeddataservice/app/src/kvstore_impl.h new file mode 100755 index 000000000..daf5eedb1 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_impl.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 KVSTORE_IMPL_H +#define KVSTORE_IMPL_H + +#include +#include +#include +#include +#include "ikvstore.h" +#include "ikvstore_observer.h" +#include "ikvstore_snapshot.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "kvstore_observer_impl.h" +#include "kvstore_snapshot_impl.h" +#include "types.h" +#include "inner_types.h" + +namespace OHOS { +namespace DistributedKv { +#define IMPORT_DATABASE(bundleName) (Import(bundleName) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED) + +struct KvStoreObserverImplPtrCompare { + bool operator()(const KvStoreObserverImpl *lhs, const KvStoreObserverImpl *rhs) const + { + if (lhs == rhs || rhs == nullptr) { + return false; + } + if (lhs == nullptr) { + return true; + } + return lhs->GetKvStoreObserverProxy()->AsObject().GetRefPtr() < + rhs->GetKvStoreObserverProxy()->AsObject().GetRefPtr(); + } +}; + +class KvStoreImpl : public KvStoreImplStub { +public: + KvStoreImpl(const Options &options, const std::string &deviceAccountId, + const std::string &bundleName, const std::string &storeId, + const std::string &appDirectory, DistributedDB::KvStoreDelegate *kvStoreDelegate); + + void GetKvStoreSnapshot(sptr observer, + std::function)> callback) override; + + Status ReleaseKvStoreSnapshot(sptr iKvStoreSnapshot) override; + + Status Put(const Key &key, const Value &value) override; + + Status PutBatch(const std::vector &entries) override; + + Status Delete(const Key &key) override; + + Status DeleteBatch(const std::vector &keys) override; + + Status Clear() override; + + Status StartTransaction() override; + + Status Commit() override; + + Status Rollback() override; + + /* subscribe kv store */ + Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer) override; + + /* unsubscribe kv store */ + Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) override; + + virtual const std::string GetStorePath(); + + virtual ~KvStoreImpl(); + + InnerStatus Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager); + + Status ForceClose(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager); + + Status MigrateKvStore(const std::string &harmonyAccountId, + const std::string &kvStoreDataDir, + DistributedDB::KvStoreDelegateManager *oldDelegateMgr, + DistributedDB::KvStoreDelegateManager *&newDelegateMgr); + + void IncreaseOpenCount(); + + Status ReKey(const std::vector &key); + + bool Import(const std::string &bundleName) const; +private: + Status RebuildKvStoreObserver(DistributedDB::KvStoreDelegate *kvStoreDelegate); + + Status RebuildKvStoreSnapshot(DistributedDB::KvStoreDelegate *kvStoreDelegate); + + // kvstore options + const Options options_; + + // device account id + std::string deviceAccountId_; + + // appId get from PMS. + const std::string bundleName_; + + // kvstore name. + const std::string storeId_; + + // kvstore absolute path in distributeddatamgr. + const std::string storePath_; + + // distributeddb is responsible for free kvStoreDelegate_, + // by calling CloseKvStore in KvStoreAppManager, + // can not free it in KvStoreImpl's destructor. + mutable std::shared_mutex storeDelegateMutex_{}; + DistributedDB::KvStoreDelegate *kvStoreDelegate_; + std::mutex storeObserverMutex_; + std::set observerSet_; + std::mutex storeSnapshotMutex_; + std::map> snapshotMap_; + int openCount_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_IMPL_H diff --git a/services/distributeddataservice/app/src/kvstore_meta_manager.cpp b/services/distributeddataservice/app/src/kvstore_meta_manager.cpp new file mode 100755 index 000000000..937ea43d2 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_meta_manager.cpp @@ -0,0 +1,1120 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "KvStoreMetaManager" + +#include "kvstore_meta_manager.h" +#include +#include +#include +#include +#include +#include +#include "security_adapter.h" + +#include "account_delegate.h" +#include "constant.h" +#include "kvstore_utils.h" +#include "crypto_utils.h" +#include "device_kvstore_impl.h" +#include "kvstore_data_service.h" +#include "log_print.h" +#include "reporter.h" +#include "directory_utils.h" + +namespace OHOS { +namespace DistributedKv { +using json = nlohmann::json; +using namespace std::chrono; + +// APPID: distributeddata +// USERID: default +// STOREID: service_meta +// dataDir: /data/misc_de/0/mdds/Meta/${storeId}/sin_gen.db +std::condition_variable KvStoreMetaManager::cv_; +std::mutex KvStoreMetaManager::cvMutex_; +KvStoreMetaManager::MetaDeviceChangeListenerImpl KvStoreMetaManager::listener_; + +KvStoreMetaManager::KvStoreMetaManager() + : metaDBDirectory_(Constant::Concatenate({ + Constant::ROOT_PATH_DE, "/", Constant::SERVICE_NAME, "/", Constant::META_DIR_NAME })), + kvStoreDelegateManager_(META_DB_APP_ID, Constant::GetDefaultHarmonyAccountName()) +{ + ZLOGI("begin."); +} + +KvStoreMetaManager::~KvStoreMetaManager() +{ +} + +KvStoreMetaManager &KvStoreMetaManager::GetInstance() +{ + static KvStoreMetaManager instance; + return instance; +} + +void KvStoreMetaManager::InitMetaListener(std::function observer) +{ + metaObserver_.notify_ = observer; + + InitMetaData(); + auto status = KvStoreUtils::GetProviderInstance().StartWatchDeviceChange(&listener_, {"metaMgr"}); + if (status != AppDistributedKv::Status::SUCCESS) { + ZLOGW("register failed."); + } + ZLOGI("register meta device change success."); + GetInstance().SubscribeMetaKvStore(); +} + +void KvStoreMetaManager::InitMetaData() +{ + ZLOGI("start."); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGI("get meta failed."); + return; + } + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + auto metaKey = GetMetaKey(AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, "default", META_DB_APP_ID, + Constant::SERVICE_META_DB_NAME); + struct KvStoreMetaData metaData { + .appId = META_DB_APP_ID, + .appType = "default", + .bundleName = META_DB_APP_ID, + .dataDir = "default", + .deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, + .deviceId = DeviceKvStoreImpl::GetLocalDeviceId(), + .isAutoSync = false, + .isBackup = false, + .isEncrypt = false, + .kvStoreType = KvStoreType::SINGLE_VERSION, + .schema = "", + .storeId = Constant::SERVICE_META_DB_NAME, + .userId = userId, + .uid = -1, + .version = KvStoreDataService::KVSTORE_META_VERSION, + .securityLevel = SecurityLevel::NO_LABEL, + }; + std::string jsonStr = metaData.Marshal(); + std::vector value(jsonStr.begin(), jsonStr.end()); + if (CheckUpdateServiceMeta(metaKey, UPDATE, value) != Status::SUCCESS) { + ZLOGW("CheckUpdateServiceMeta database failed."); + } + ZLOGI("end."); +} + +void KvStoreMetaManager::InitMetaParameter() +{ + ZLOGI("start."); + + bool ret = ForceCreateDirectory(metaDBDirectory_); + if (!ret) { + FaultMsg msg = {FaultType::SERVICE_FAULT, "user", __FUNCTION__, Fault::SF_CREATE_DIR}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + ZLOGE("create directories failed"); + return; + } + // change mode for directories to 0755, and for files to 0600. + DirectoryUtils::ChangeModeDirOnly(metaDBDirectory_, Constant::DEFAULT_MODE_DIR); + DirectoryUtils::ChangeModeFileOnly(metaDBDirectory_, Constant::DEFAULT_MODE_FILE); + + DistributedDB::KvStoreConfig kvStoreConfig {metaDBDirectory_}; + kvStoreDelegateManager_.SetKvStoreConfig(kvStoreConfig); +} + +const KvStoreMetaManager::NbDelegate &KvStoreMetaManager::GetMetaKvStore() +{ + if (metaDelegate_ == nullptr) { + metaDelegate_ = CreateMetaKvStore(); + } + return metaDelegate_; +} + +KvStoreMetaManager::NbDelegate KvStoreMetaManager::CreateMetaKvStore() +{ + DistributedDB::DBStatus dbStatusTmp = DistributedDB::DBStatus::NOT_SUPPORT; + DistributedDB::KvStoreNbDelegate::Option option; + option.createIfNecessary = true; + option.isMemoryDb = false; + option.createDirByStoreIdOnly = true; + option.isEncryptedDb = false; + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegatePtr = nullptr; + kvStoreDelegateManager_.GetKvStore( + Constant::SERVICE_META_DB_NAME, option, + [&kvStoreNbDelegatePtr, &dbStatusTmp](DistributedDB::DBStatus dbStatus, + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) { + kvStoreNbDelegatePtr = kvStoreNbDelegate; + dbStatusTmp = dbStatus; + }); + + if (dbStatusTmp != DistributedDB::DBStatus::OK) { + ZLOGE("GetKvStore return error status: %d", static_cast(dbStatusTmp)); + return nullptr; + } + auto release = [this](DistributedDB::KvStoreNbDelegate *delegate) { + if (delegate == nullptr) { + return; + } + + auto result = kvStoreDelegateManager_.CloseKvStore(delegate); + if (result != DistributedDB::DBStatus::OK) { + ZLOGE("CloseMetaKvStore return error status: %d", static_cast(result)); + } + }; + return NbDelegate(kvStoreNbDelegatePtr, release); +} + +std::vector KvStoreMetaManager::GetMetaKey(const std::string &deviceAccountId, + const std::string &groupId, const std::string &bundleName, + const std::string &storeId, const std::string &key) +{ + std::string originKey; + if (key.empty()) { + originKey = DeviceKvStoreImpl::GetLocalDeviceId() + Constant::KEY_SEPARATOR + + deviceAccountId + Constant::KEY_SEPARATOR + + groupId + Constant::KEY_SEPARATOR + + bundleName + Constant::KEY_SEPARATOR + + storeId; + return KvStoreMetaRow::GetKeyFor(originKey); + } + + originKey = deviceAccountId + Constant::KEY_SEPARATOR + + groupId + Constant::KEY_SEPARATOR + + bundleName + Constant::KEY_SEPARATOR + + storeId + Constant::KEY_SEPARATOR + + key; + return SecretMetaRow::GetKeyFor(originKey); +} + +std::string KvStoreMetaManager::GetSecretKeyFile(const std::string &deviceAccountId, const std::string &appId, + const std::string &storeId) +{ + std::string hashedStoreId; + DistributedDB::DBStatus result = DistributedDB::KvStoreDelegateManager::GetDatabaseDir(storeId, hashedStoreId); + if (DistributedDB::OK != result) { + ZLOGE("get data base directory by kvstore store id failed, result = %d.", result); + return ""; + } + return Constant::ROOT_PATH_DE + "/" + Constant::SERVICE_NAME + "/" + + deviceAccountId + "/" + Constant::GetDefaultHarmonyAccountName() + "/" + + appId + "/" + hashedStoreId + ".mul.key"; +} + +std::string KvStoreMetaManager::GetSecretSingleKeyFile(const std::string &deviceAccountId, const std::string &appId, + const std::string &storeId) +{ + std::string hashedStoreId; + DistributedDB::DBStatus result = DistributedDB::KvStoreDelegateManager::GetDatabaseDir(storeId, hashedStoreId); + if (DistributedDB::OK != result) { + ZLOGE("get data base directory by kvstore store id failed, result = %d.", result); + return ""; + } + return Constant::ROOT_PATH_DE + "/" + Constant::SERVICE_NAME + "/" + + deviceAccountId + "/" + Constant::GetDefaultHarmonyAccountName() + "/" + + appId + "/" + hashedStoreId + ".sig.key"; +} + +Status KvStoreMetaManager::CheckUpdateServiceMeta(const std::vector &metaKey, FLAG flag, + const std::vector &val) +{ + ZLOGD("begin."); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + KvStoreAppManager::PathType pathType = KvStoreAppManager::PATH_CE; + DistributedDB::Key dbKey = metaKey; + DistributedDB::Value dbValue = val; + DistributedDB::DBStatus dbStatus; + DistributedDB::CipherPassword dbPassword; + const std::string deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + switch (flag) { + case UPDATE: + dbStatus = metaDelegate->Put(dbKey, dbValue); + metaDelegate->Export(BackupHandler::GetBackupPath(deviceAccountId, pathType), dbPassword); + break; + case DELETE: + dbStatus = metaDelegate->Delete(dbKey); + metaDelegate->Export(BackupHandler::GetBackupPath(deviceAccountId, pathType), dbPassword); + break; + case CHECK_EXIST: + dbStatus = metaDelegate->Get(dbKey, dbValue); + break; + case UPDATE_LOCAL: + dbStatus = metaDelegate->PutLocal(dbKey, dbValue); + metaDelegate->Export(BackupHandler::GetBackupPath(deviceAccountId, pathType), dbPassword); + break; + case DELETE_LOCAL: + dbStatus = metaDelegate->DeleteLocal(dbKey); + metaDelegate->Export(BackupHandler::GetBackupPath(deviceAccountId, pathType), dbPassword); + break; + case CHECK_EXIST_LOCAL: + dbStatus = metaDelegate->GetLocal(dbKey, dbValue); + break; + default: + break; + } + ZLOGI("Flag: %d status: %d", static_cast(flag), static_cast(dbStatus)); + SyncMeta(); + return (dbStatus != DistributedDB::DBStatus::OK) ? Status::DB_ERROR : Status::SUCCESS; +} + +Status KvStoreMetaManager::GenerateRootKey() +{ + return Status::ERROR; +} + +Status KvStoreMetaManager::CheckRootKeyExist() +{ + ZLOGI("GenerateRootKey."); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + DistributedDB::Key dbKey = std::vector(Constant::ROOT_KEY_GENERATED.begin(), + Constant::ROOT_KEY_GENERATED.end()); + DistributedDB::Value dbValue; + if (metaDelegate->GetLocal(dbKey, dbValue) == DistributedDB::DBStatus::OK) { + ZLOGI("root key exist."); + return Status::SUCCESS; + } + return Status::ERROR; +} + +std::vector KvStoreMetaManager::EncryptWorkKey(const std::vector &key) +{ + std::vector encryptedKeyVec; + return encryptedKeyVec; +} + +bool KvStoreMetaManager::DecryptWorkKey(const std::vector &encryptedKey, std::vector &key) +{ + return false; +} + +Status KvStoreMetaManager::WriteSecretKeyToMeta(const std::vector &metaKey, const std::vector &key) +{ + ZLOGD("start"); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + SecretKeyMetaData secretKey; + secretKey.kvStoreType = KvStoreType::DEVICE_COLLABORATION; + secretKey.timeValue = TransferTypeToByteArray(system_clock::to_time_t(system_clock::now())); + secretKey.secretKey = EncryptWorkKey(key); + if (secretKey.secretKey.empty()) { + ZLOGE("encrypt work key error."); + return Status::CRYPT_ERROR; + } + + DistributedDB::DBStatus dbStatus = metaDelegate->PutLocal(metaKey, secretKey); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("end with %d", static_cast(dbStatus)); + return Status::DB_ERROR; + } else { + ZLOGD("normal end"); + return Status::SUCCESS; + } +} + +Status KvStoreMetaManager::WriteSecretKeyToFile(const std::string &secretKeyFile, const std::vector &key) +{ + ZLOGD("start"); + std::vector secretKey = EncryptWorkKey(key); + if (secretKey.empty()) { + ZLOGW("encrypt work key error."); + return Status::CRYPT_ERROR; + } + std::string dbDir = secretKeyFile.substr(0, secretKeyFile.find_last_of('/')); + if (!ForceCreateDirectory(dbDir)) { + return Status::ERROR; + } + + std::vector secretKeyInByte = + TransferTypeToByteArray(system_clock::to_time_t(system_clock::now())); + std::vector secretKeyInChar; + secretKeyInChar.insert(secretKeyInChar.end(), secretKeyInByte.begin(), secretKeyInByte.end()); + secretKeyInChar.insert(secretKeyInChar.end(), secretKey.begin(), secretKey.end()); + if (SaveBufferToFile(secretKeyFile, secretKeyInChar)) { + ZLOGD("normal end"); + return Status::SUCCESS; + } + ZLOGW("failure end"); + return Status::ERROR; +} + +Status KvStoreMetaManager::RemoveSecretKey(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId) +{ + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + Status status = Status::SUCCESS; + DistributedDB::Key secretDbKey = GetMetaKey(deviceAccountId, "default", bundleName, storeId, "KEY"); + DistributedDB::Key secretSingleDbKey = GetMetaKey(deviceAccountId, "default", bundleName, storeId, "SINGLE_KEY"); + DistributedDB::DBStatus dbStatus = metaDelegate->DeleteLocal(secretDbKey); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delete secretDbKey fail Status %d", static_cast(dbStatus)); + status = Status::DB_ERROR; + } + dbStatus = metaDelegate->DeleteLocal(secretSingleDbKey); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delete secretSingleDbKey fail Status %d", static_cast(dbStatus)); + status = Status::DB_ERROR; + } + + std::string secretKeyFile = GetSecretKeyFile(deviceAccountId, bundleName, storeId); + bool rmFile = RemoveFile(secretKeyFile); + if (!rmFile) { + ZLOGW("remove secretKeyFile fail."); + status = Status::DB_ERROR; + } + secretKeyFile = GetSecretSingleKeyFile(deviceAccountId, bundleName, storeId); + rmFile = RemoveFile(secretKeyFile); + if (!rmFile) { + ZLOGW("remove secretKeyFile Single fail."); + status = Status::DB_ERROR; + } + return status; +} + +Status KvStoreMetaManager::GetSecretKeyFromMeta(const std::vector &metaSecretKey, std::vector &key, + bool &outdated) +{ + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + DistributedDB::Key dbKey = metaSecretKey; + DistributedDB::Value dbValue; + DistributedDB::DBStatus dbStatus = metaDelegate->GetLocal(dbKey, dbValue); + if (dbStatus != DistributedDB::DBStatus::OK) { + return Status::DB_ERROR; + } + std::string jsonStr(dbValue.begin(), dbValue.end()); + json jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + ZLOGE("parse json error"); + return Status::ERROR; + } + SecretKeyMetaData sKeyValue(jsonObj); + time_t createTime = TransferByteArrayToType(sKeyValue.timeValue); + DecryptWorkKey(sKeyValue.secretKey, key); + system_clock::time_point createTimeChrono = system_clock::from_time_t(createTime); + outdated = ((createTimeChrono + hours(HOURS_PER_YEAR)) < system_clock::now()); // secretKey valid for 1 year. + return Status::SUCCESS; +} + +Status KvStoreMetaManager::RecoverSecretKeyFromFile(const std::string &secretKeyFile, + const std::vector &metaSecretKey, + std::vector &key, bool &outdated) +{ + std::vector fileBuffer; + if (!LoadBufferFromFile(secretKeyFile, fileBuffer)) { + return Status::ERROR; + } + if (fileBuffer.size() < sizeof(time_t) / sizeof(uint8_t) + KEY_SIZE) { + return Status::ERROR; + } + std::vector timeVec; + auto iter = fileBuffer.begin(); + for (int i = 0; i < static_cast(sizeof(time_t) / sizeof(uint8_t)); i++) { + timeVec.push_back(*iter); + iter++; + } + time_t createTime = TransferByteArrayToType(timeVec); + SecretKeyMetaData secretKey; + secretKey.secretKey.insert(secretKey.secretKey.end(), iter, fileBuffer.end()); + if (!DecryptWorkKey(secretKey.secretKey, key)) { + return Status::ERROR; + } + system_clock::time_point createTimeChrono = system_clock::from_time_t(createTime); + outdated = ((createTimeChrono + hours(HOURS_PER_YEAR)) < system_clock::now()); // secretKey valid for 1 year. + + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + + secretKey.timeValue = TransferTypeToByteArray(createTime); + secretKey.kvStoreType = KvStoreType::DEVICE_COLLABORATION; + + DistributedDB::DBStatus dbStatus = metaDelegate->PutLocal(metaSecretKey, secretKey); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("put work key failed."); + return Status::DB_ERROR; + } + return Status::SUCCESS; +} + +void KvStoreMetaManager::ReKey(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId, sptr store) +{ + if (store == nullptr) { + return; + } + KvStoreImpl *kvStoreimpl = static_cast(store.GetRefPtr()); + std::vector key; + CryptoUtils::GetRandomKey(KEY_SIZE, key); + WriteSecretKeyToMeta(GetMetaKey(deviceAccountId, "default", bundleName, storeId, "KEY"), key); + Status status = kvStoreimpl->ReKey(key); + if (status == Status::SUCCESS) { + WriteSecretKeyToFile(GetSecretKeyFile(deviceAccountId, bundleName, storeId), key); + } + key.assign(key.size(), 0); +} + +void KvStoreMetaManager::ReKey(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId, sptr store) +{ + if (store == nullptr) { + return; + } + SingleKvStoreImpl *kvStoreImpl = static_cast(store.GetRefPtr()); + std::vector key; + CryptoUtils::GetRandomKey(KEY_SIZE, key); + WriteSecretKeyToMeta(GetMetaKey(deviceAccountId, "default", bundleName, storeId, "SINGLE_KEY"), key); + Status status = kvStoreImpl->ReKey(key); + if (status == Status::SUCCESS) { + WriteSecretKeyToFile(GetSecretSingleKeyFile(deviceAccountId, bundleName, storeId), key); + } + key.assign(key.size(), 0); +} + +// StrategyMetaData###deviceId###deviceAccountID###${groupId}###bundleName###storeId +void KvStoreMetaManager::GetStrategyMetaKey(const StrategyMeta ¶ms, std::string &retVal) +{ + std::vector keys = {STRATEGY_META_PREFIX, params.devId, params.devAccId, params.grpId, + params.bundleName, params.storeId}; + ConcatWithSharps(keys, retVal); +} + +void KvStoreMetaManager::ConcatWithSharps(const std::vector ¶ms, std::string &retVal) +{ + int32_t len = static_cast(params.size()); + for (int32_t i = 0; i < len; i++) { + retVal.append(params.at(i)); + if (i != (len - 1)) { + retVal.append(Constant::KEY_SEPARATOR); + } + } +} + +Status KvStoreMetaManager::SaveStrategyMetaEnable(const std::string &key, bool enable) +{ + ZLOGD("begin"); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + return Status::ERROR; + } + auto dbkey = std::vector(key.begin(), key.end()); + std::vector values; + auto dbStatus = metaDelegate->Get(dbkey, values); + if (dbStatus == DistributedDB::DBStatus::NOT_FOUND) { + json j; + j[CAPABILITY_ENABLED] = enable; + std::string json = j.dump(); + if (metaDelegate->Put(dbkey, std::vector(json.begin(), json.end())) != DistributedDB::OK) { + ZLOGE("save failed."); + return Status::DB_ERROR; + } + ZLOGD("save end"); + } else if (dbStatus == DistributedDB::DBStatus::OK) { + std::string jsonStr(values.begin(), values.end()); + auto jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + ZLOGE("invalid json."); + return Status::ERROR; + } + jsonObj[CAPABILITY_ENABLED] = enable; + std::string json = jsonObj.dump(); + if (metaDelegate->Put(dbkey, std::vector(json.begin(), json.end())) != DistributedDB::OK) { + ZLOGE("save failed."); + return Status::DB_ERROR; + } + ZLOGD("update end"); + } else { + ZLOGE("failed."); + return Status::DB_ERROR; + } + SyncMeta(); + return Status::SUCCESS; +} + +Status KvStoreMetaManager::SaveStrategyMetaLabels(const std::string &key, + const std::vector &localLabels, + const std::vector &remoteSupportLabels) +{ + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + return Status::ERROR; + } + auto dbkey = std::vector(key.begin(), key.end()); + std::vector values; + auto dbStatus = metaDelegate->Get(dbkey, values); + if (dbStatus == DistributedDB::DBStatus::NOT_FOUND) { + json j; + j[CAPABILITY_RANGE][LOCAL_LABEL] = localLabels; + j[CAPABILITY_RANGE][REMOTE_LABEL] = remoteSupportLabels; + std::string metaJson = j.dump(); + if (metaDelegate->Put(dbkey, std::vector(metaJson.begin(), metaJson.end())) != DistributedDB::OK) { + ZLOGE("save failed."); + return Status::DB_ERROR; + } + } else if (dbStatus == DistributedDB::DBStatus::OK) { + std::string jsonStr(values.begin(), values.end()); + auto j = json::parse(jsonStr, nullptr, false); + if (j.is_discarded()) { + return Status::ERROR; + } + j[CAPABILITY_RANGE][LOCAL_LABEL] = localLabels; + j[CAPABILITY_RANGE][REMOTE_LABEL] = remoteSupportLabels; + std::string metaJson = j.dump(); + if (metaDelegate->Put(dbkey, std::vector(metaJson.begin(), metaJson.end())) != DistributedDB::OK) { + ZLOGE("save failed."); + return Status::DB_ERROR; + } + } else { + ZLOGE("failed."); + return Status::DB_ERROR; + } + SyncMeta(); + return Status::SUCCESS; +} + +Status KvStoreMetaManager::DeleteStrategyMeta(const std::string &bundleName, const std::string &storeId) +{ + ZLOGI("start"); + std::string key; + std::string devId = DeviceKvStoreImpl::GetLocalDeviceId(); + if (devId.empty()) { + ZLOGE("get device id empty."); + return Status::ERROR; + } + std::string devAccId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + StrategyMeta params = {devId, devAccId, Constant::DEFAULT_GROUP_ID, bundleName, storeId}; + GetStrategyMetaKey(params, key); + + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + return Status::ERROR; + } + auto dbkey = std::vector(key.begin(), key.end()); + auto dbStatus = metaDelegate->Delete(dbkey); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("failed."); + return Status::DB_ERROR; + } + return Status::SUCCESS; +} + +void KvStoreMetaManager::SyncMeta() +{ + std::vector devs; + auto deviceList = KvStoreUtils::GetProviderInstance().GetDeviceList(); + for (auto const &dev : deviceList) { + devs.push_back(dev.deviceId); + } + + if (devs.empty()) { + ZLOGW("meta db sync fail, devices is empty."); + return; + } + + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGW("meta db sync failed."); + return; + } + auto onComplete = [this](const std::map &) { + ZLOGD("meta db sync complete."); + cv_.notify_all(); + ZLOGD("meta db sync complete end."); + }; + auto dbStatus = metaDelegate->Sync(devs, DistributedDB::SyncMode::SYNC_MODE_PUSH_PULL, onComplete); + if (dbStatus != DistributedDB::OK) { + ZLOGW("meta db sync error %d.", dbStatus); + } +} + +void KvStoreMetaManager::SubscribeMetaKvStore() +{ + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGW("register meta observer failed."); + return; + } + + int mode = DistributedDB::OBSERVER_CHANGES_NATIVE; + auto dbStatus = metaDelegate->RegisterObserver(DistributedDB::Key(), mode, &metaObserver_); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGW("register meta observer failed :%d.", dbStatus); + } +} + +Status KvStoreMetaManager::CheckSyncPermission(const std::string &userId, const std::string &appId, + const std::string &storeId, uint8_t flag, const std::string &deviceId) +{ + std::string devId = DeviceKvStoreImpl::GetLocalDeviceId(); + if (devId.empty()) { + ZLOGE("get device id empty."); + return Status::ERROR; + } + KvStoreMetaData val; + auto queryStatus = QueryKvStoreMetaDataByDeviceIdAndAppId(devId, appId, val); + if (queryStatus != Status::SUCCESS) { + ZLOGE("get kvstore by deviceId and appId empty."); + return Status::ERROR; + } + + std::string devAccId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + StrategyMeta params = {devId, devAccId, Constant::DEFAULT_GROUP_ID, val.bundleName, storeId}; + std::string localKey; + GetStrategyMetaKey(params, localKey); + if (localKey.empty()) { + ZLOGE("get key empty."); + return Status::ERROR; + } + + std::string remoteKey; + params.devId = deviceId; + GetStrategyMetaKey(params, remoteKey); + if (remoteKey.empty()) { + ZLOGE("get key empty."); + return Status::ERROR; + } + + std::map> localStrategies; + std::map> remoteStrategies; + GetStategyMeta(localKey, localStrategies); + GetStategyMeta(remoteKey, remoteStrategies); + if (localStrategies.empty() || remoteStrategies.empty()) { + ZLOGD("no range, sync permission success."); + return Status::SUCCESS; + } + + auto localSupportRemotes = localStrategies.find(REMOTE_LABEL); + auto remoteSupportLocals = remoteStrategies.find(LOCAL_LABEL); + if (localSupportRemotes != localStrategies.end() && remoteSupportLocals != remoteStrategies.end()) { + std::vector lremotes = localSupportRemotes->second; + for (auto const &lremote : lremotes) { + std::vector rlocals = remoteSupportLocals->second; + if (std::find(rlocals.begin(), rlocals.end(), lremote) != rlocals.end()) { + ZLOGD("find range, sync permission success."); + return Status::SUCCESS; + } + } + } + ZLOGD("check strategy failed, sync permission fail."); + return Status::ERROR; +} + +Status KvStoreMetaManager::GetStategyMeta(const std::string &key, + std::map> &strategies) +{ + ZLOGD("get meta key:%s.", key.c_str()); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGW("get delegate error."); + return Status::ERROR; + } + + DistributedDB::Value values; + auto dbStatus = metaDelegate->Get(DistributedDB::Key(key.begin(), key.end()), values); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGW("get meta error %d.", dbStatus); + return Status::DB_ERROR; + } + + std::string jsonStr(values.begin(), values.end()); + auto jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + jsonObj = json::parse(jsonStr.substr(1), nullptr, false); // 1 drop for a + if (jsonObj.is_discarded()) { + ZLOGW("get meta parse error."); + return Status::ERROR; + } + } + + auto range = jsonObj.find(CAPABILITY_RANGE); + if (range == jsonObj.end()) { + ZLOGW("get meta parse no range."); + return Status::ERROR; + } + + auto local = range->find(LOCAL_LABEL); + if (local != range->end()) { + json obj = *local; + if (obj.is_array()) { + std::vector v; + obj.get_to(v); + strategies.insert({LOCAL_LABEL, v}); + } + } + auto remote = range->find(REMOTE_LABEL); + if (remote != range->end()) { + json obj = *remote; + if (obj.is_array()) { + std::vector v; + obj.get_to(v); + strategies.insert({REMOTE_LABEL, v}); + } + } + return Status::SUCCESS; +} + +KvStoreMetaManager::KvStoreMetaObserver::~KvStoreMetaObserver() +{ + ZLOGW("meta observer destruct."); +} + +void KvStoreMetaManager::KvStoreMetaObserver::OnChange(const DistributedDB::KvStoreChangedData &data) +{ + ZLOGD("on data change."); + if (notify_ != nullptr) { + auto &updated = data.GetEntriesUpdated(); + for (const auto &entry : updated) { + std::string key(entry.key.begin(), entry.key.end()); + if (key.find(KvStoreMetaRow::KEY_PREFIX) != 0) { + continue; + } + + KvStoreMetaData metaData; + std::string json(entry.value.begin(), entry.value.end()); + metaData.Unmarshal(Serializable::ToJson(json)); + ZLOGD("meta data info appType:%s, storeId:%s isDirty:%d", + metaData.appType.c_str(), metaData.storeId.c_str(), metaData.isDirty); + if (!metaData.isDirty || metaData.appType != HARMONY_APP) { + continue; + } + ZLOGI("dirty kv store. storeId:%s", metaData.storeId.c_str()); + notify_(metaData); + } + } + KvStoreMetaManager::GetInstance().SyncMeta(); +} + +void KvStoreMetaManager::MetaDeviceChangeListenerImpl::OnDeviceChanged( + const AppDistributedKv::DeviceInfo &info, const AppDistributedKv::DeviceChangeType &type) const +{ + if (type == AppDistributedKv::DeviceChangeType::DEVICE_OFFLINE) { + ZLOGD("offline ignore."); + return; + } + + ZLOGD("begin to sync."); + KvStoreMetaManager::GetInstance().SyncMeta(); + ZLOGD("end."); +} + +AppDistributedKv::ChangeLevelType KvStoreMetaManager::MetaDeviceChangeListenerImpl::GetChangeLevelType() const +{ + return AppDistributedKv::ChangeLevelType::HIGH; +} + +void KvStoreMetaManager::ToJson(json &j, const KvStoreMetaData &k) +{ + j = json(k.Marshal()); +} + +void KvStoreMetaManager::FromJson(const json &j, KvStoreMetaData &k) +{ + k.Unmarshal(j); +} + +Status KvStoreMetaManager::QueryKvStoreMetaDataByDeviceIdAndAppId(const std::string &devId, const std::string &appId, + KvStoreMetaData &val) +{ + ZLOGD("query meta start."); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGW("get delegate error."); + return Status::ERROR; + } + std::string dbPrefixKey; + std::string prefix = KvStoreMetaRow::KEY_PREFIX; + ConcatWithSharps({prefix, devId}, dbPrefixKey); + std::vector values; + auto status = metaDelegate->GetEntries(DistributedDB::Key(dbPrefixKey.begin(), dbPrefixKey.end()), values); + if (status != DistributedDB::DBStatus::OK) { + status = metaDelegate->GetEntries(DistributedDB::Key(prefix.begin(), prefix.end()), values); + if (status != DistributedDB::DBStatus::OK) { + ZLOGW("query db failed key:%s, ret:%d.", dbPrefixKey.c_str(), static_cast(status)); + return Status::ERROR; + } + } + + for (auto const &entry : values) { + std::string str(entry.value.begin(), entry.value.end()); + json j = Serializable::ToJson(str); + val.Unmarshal(j); + if (val.appId == appId) { + ZLOGD("query meta success."); + return Status::SUCCESS; + } + } + + ZLOGW("find meta failed id:%s.", appId.c_str()); + return Status::ERROR; +} + +Status KvStoreMetaManager::GetKvStoreMeta(const std::vector &metaKey, KvStoreMetaData &metaData) +{ + ZLOGD("begin."); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + ZLOGE("GetMetaKvStore return nullptr."); + return Status::DB_ERROR; + } + DistributedDB::Value dbValue; + DistributedDB::DBStatus dbStatus = metaDelegate->Get(metaKey, dbValue); + ZLOGI("status: %d", static_cast(dbStatus)); + if (dbStatus == DistributedDB::DBStatus::NOT_FOUND) { + ZLOGI("key not found."); + return Status::KEY_NOT_FOUND; + } + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("GetKvStoreMeta failed."); + return Status::DB_ERROR; + } + + std::string jsonStr(dbValue.begin(), dbValue.end()); + metaData.Unmarshal(Serializable::ToJson(jsonStr)); + return Status::SUCCESS; +} + +std::string KvStoreMetaData::Marshal() const +{ + json jval = { + {DEVICE_ID, deviceId}, + {USER_ID, userId}, + {APP_ID, appId}, + {STORE_ID, storeId}, + {BUNDLE_NAME, bundleName}, + {KVSTORE_TYPE, kvStoreType}, + {ENCRYPT, isEncrypt}, + {BACKUP, isBackup}, + {AUTO_SYNC, isAutoSync}, + {SCHEMA, schema}, + {DATA_DIR, dataDir}, // Reserved for kvstore data storage directory. + {APP_TYPE, appType}, // Reserved for the APP type which used kvstore. + {DEVICE_ACCOUNT_ID, deviceAccountId}, + {UID, uid}, + {VERSION, version}, + {SECURITY_LEVEL, securityLevel}, + {DIRTY_KEY, isDirty}, + }; + return jval.dump(); +} + +json Serializable::ToJson(const std::string &jsonStr) +{ + json jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + // if the string size is less than 1, means the string is invalid. + if (jsonStr.empty()) { + ZLOGE("empty jsonStr, error."); + return {}; + } + jsonObj = json::parse(jsonStr.substr(1), nullptr, false); // drop first char to adapt A's value; + if (jsonObj.is_discarded()) { + ZLOGE("parse jsonStr, error."); + return {}; + } + } + return jsonObj; +} + +void KvStoreMetaData::Unmarshal(const nlohmann::json &jObject) +{ + kvStoreType = Serializable::GetVal(jObject, KVSTORE_TYPE, json::value_t::number_unsigned, kvStoreType); + isBackup = Serializable::GetVal(jObject, BACKUP, json::value_t::boolean, isBackup); + isEncrypt = Serializable::GetVal(jObject, ENCRYPT, json::value_t::boolean, isEncrypt); + isAutoSync = Serializable::GetVal(jObject, AUTO_SYNC, json::value_t::boolean, isAutoSync); + appId = Serializable::GetVal(jObject, APP_ID, json::value_t::string, appId); + userId = Serializable::GetVal(jObject, USER_ID, json::value_t::string, userId); + storeId = Serializable::GetVal(jObject, STORE_ID, json::value_t::string, storeId); + bundleName = Serializable::GetVal(jObject, BUNDLE_NAME, json::value_t::string, bundleName); + deviceAccountId = Serializable::GetVal(jObject, DEVICE_ACCOUNT_ID, json::value_t::string, + deviceAccountId); + dataDir = Serializable::GetVal(jObject, DATA_DIR, json::value_t::string, dataDir); + appType = Serializable::GetVal(jObject, APP_TYPE, json::value_t::string, appType); + deviceId = Serializable::GetVal(jObject, DEVICE_ID, json::value_t::string, deviceId); + schema = Serializable::GetVal(jObject, SCHEMA, json::value_t::string, schema); + uid = Serializable::GetVal(jObject, UID, json::value_t::number_unsigned, uid); + version = Serializable::GetVal(jObject, VERSION, json::value_t::number_unsigned, version); + securityLevel = Serializable::GetVal(jObject, SECURITY_LEVEL, json::value_t::number_unsigned, + securityLevel); + isDirty = Serializable::GetVal(jObject, DIRTY_KEY, json::value_t::boolean, isDirty); +} + +bool KvStoreMetaData::CheckChiefValues(const nlohmann::json &jObject) +{ + return Serializable::CheckJsonValue(jObject, KVSTORE_TYPE, json::value_t::number_unsigned) && + Serializable::CheckJsonValue(jObject, BACKUP, json::value_t::boolean) && + Serializable::CheckJsonValue(jObject, ENCRYPT, json::value_t::boolean) && + Serializable::CheckJsonValue(jObject, AUTO_SYNC, json::value_t::boolean) && + Serializable::CheckJsonValue(jObject, APP_ID, json::value_t::string) && + Serializable::CheckJsonValue(jObject, USER_ID, json::value_t::string) && + Serializable::CheckJsonValue(jObject, STORE_ID, json::value_t::string) && + Serializable::CheckJsonValue(jObject, BUNDLE_NAME, json::value_t::string) && + Serializable::CheckJsonValue(jObject, DEVICE_ACCOUNT_ID, json::value_t::string) && + Serializable::CheckJsonValue(jObject, DATA_DIR, json::value_t::string); +} + +bool Serializable::CheckJsonValue(const nlohmann::json &j, const std::string &name, json::value_t type) +{ + auto it = j.find(name); + return it != j.end() && it->type() == type; +} + +template +T Serializable::GetVal(const json &j, const std::string &name, json::value_t type, const T &val) +{ + auto it = j.find(name); + if (it != j.end() && it->type() == type) { + return *it; + } + ZLOGW("not found name:%s.", name.c_str()); + return val; +} + +std::vector SecretKeyMetaData::Marshal() const +{ + json jval = { + {TIME, timeValue}, + {SKEY, secretKey}, + {KVSTORE_TYPE, kvStoreType} + }; + auto value = jval.dump(); + return std::vector(value.begin(), value.end()); +} + +void SecretKeyMetaData::Unmarshal(const nlohmann::json &jObject) +{ + timeValue = Serializable::GetVal>(jObject, TIME, json::value_t::array, timeValue); + secretKey = Serializable::GetVal>(jObject, SKEY, json::value_t::array, secretKey); + kvStoreType = Serializable::GetVal(jObject, KVSTORE_TYPE, json::value_t::number_unsigned, kvStoreType); +} + +bool KvStoreMetaManager::GetFullMetaData(std::map &entries) +{ + ZLOGI("start"); + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + return false; + } + + std::vector kvStoreMetaEntries; + const std::string &metaKey = KvStoreMetaRow::KEY_PREFIX; + DistributedDB::DBStatus dbStatus = metaDelegate->GetEntries({metaKey.begin(), metaKey.end()}, kvStoreMetaEntries); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Get kvstore meta data entries from metaDB failed, dbStatus: %d.", static_cast(dbStatus)); + return false; + } + + for (auto const &kvStoreMeta : kvStoreMetaEntries) { + std::string jsonStr(kvStoreMeta.value.begin(), kvStoreMeta.value.end()); + ZLOGD("kvStoreMetaData get json: %s", jsonStr.c_str()); + auto metaObj = Serializable::ToJson(jsonStr); + MetaData metaData {0}; + metaData.kvStoreType = MetaData::GetKvStoreType(metaObj); + if (metaData.kvStoreType == KvStoreType::INVALID_TYPE) { + ZLOGE("Failed to find KVSTORE_TYPE in jsonStr."); + continue; + } + + metaData.kvStoreMetaData.Unmarshal(metaObj); + std::vector decryptKey; + if (metaData.kvStoreMetaData.isEncrypt) { + ZLOGE("isEncrypt."); + const std::string keyType = ((metaData.kvStoreType == KvStoreType::SINGLE_VERSION) ? "SINGLE_KEY" : "KEY"); + const std::vector metaSecretKey = KvStoreMetaManager::GetInstance().GetMetaKey( + metaData.kvStoreMetaData.deviceAccountId, "default", metaData.kvStoreMetaData.bundleName, + metaData.kvStoreMetaData.storeId, keyType); + DistributedDB::Value secretValue; + metaDelegate->GetLocal(metaSecretKey, secretValue); + auto secretObj = Serializable::ToJson({secretValue.begin(), secretValue.end()}); + if (secretObj.empty()) { + ZLOGE("Failed to find SKEY in SecretKeyMetaData."); + continue; + } + metaData.secretKeyMetaData.Unmarshal(secretObj); + KvStoreMetaManager::GetInstance().DecryptWorkKey(metaData.secretKeyMetaData.secretKey, decryptKey); + } + entries.insert({{kvStoreMeta.key.begin(), kvStoreMeta.key.end()}, {metaData}}); + std::fill(decryptKey.begin(), decryptKey.end(), 0); + } + + return true; +} + +bool KvStoreMetaManager::GetKvStoreMetaByType(const std::string &name, const std::string &val, + KvStoreMetaData &metaData) +{ + auto &metaDelegate = GetMetaKvStore(); + if (metaDelegate == nullptr) { + return false; + } + + DistributedDB::Key metaKeyPrefix = KvStoreMetaRow::GetKeyFor(KvStoreMetaRow::KEY_PREFIX); + std::vector metaEntries; + DistributedDB::DBStatus dbStatus = metaDelegate->GetEntries(metaKeyPrefix, metaEntries); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("Get meta entries from metaDB failed, dbStatus: %d.", static_cast(dbStatus)); + return false; + } + + for (auto const &metaEntry : metaEntries) { + std::string jsonStr(metaEntry.value.begin(), metaEntry.value.end()); + ZLOGD("KvStore get json: %s", jsonStr.c_str()); + json jsonObj = json::parse(jsonStr, nullptr, false); + if (jsonObj.is_discarded()) { + ZLOGE("parse json error"); + continue; + } + + std::string metaTypeVal; + jsonObj[name].get_to(metaTypeVal); + if (metaTypeVal == val) { + metaData.Unmarshal(Serializable::ToJson(jsonStr)); + } + } + return true; +} + +bool KvStoreMetaManager::GetKvStoreMetaDataByBundleName(const std::string &bundleName, KvStoreMetaData &metaData) +{ + return GetKvStoreMetaByType(KvStoreMetaData::BUNDLE_NAME, bundleName, metaData); +} + +bool KvStoreMetaManager::GetKvStoreMetaDataByAppId(const std::string &appId, KvStoreMetaData &metaData) +{ + return GetKvStoreMetaByType(KvStoreMetaData::APP_ID, appId, metaData); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_meta_manager.h b/services/distributeddataservice/app/src/kvstore_meta_manager.h new file mode 100755 index 000000000..3c70189e9 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_meta_manager.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_META_MANAGER_H +#define KVSTORE_META_MANAGER_H + +#include +#include + +#include "app_device_status_change_listener.h" +#include "types.h" +#include "system_ability.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "kvstore_impl.h" +#include "single_kvstore_impl.h" + +namespace OHOS { +namespace DistributedKv { +enum FLAG { + UPDATE, + DELETE, + CHECK_EXIST, + UPDATE_LOCAL, + DELETE_LOCAL, + CHECK_EXIST_LOCAL, +}; + +struct Serializable { + using json = nlohmann::json; + template + static T GetVal(const json &j, const std::string &name, json::value_t type, const T &def); + static bool CheckJsonValue(const json &j, const std::string &name, json::value_t type); + static json ToJson(const std::string &jsonStr); +}; + +struct StrategyMeta { + std::string devId; + std::string devAccId; + std::string grpId; + std::string bundleName; + std::string storeId; +}; + +struct SecretKeyMetaData { + static constexpr const char *SKEY = "skey"; + std::vector timeValue {}; + std::vector secretKey {}; + KvStoreType kvStoreType = KvStoreType::INVALID_TYPE; + SecretKeyMetaData() {} + explicit SecretKeyMetaData(const nlohmann::json &jObject) + { + Unmarshal(jObject); + } + + std::vector Marshal() const; + void Unmarshal(const nlohmann::json &jObject); + operator std::vector() const + { + return Marshal(); + } +private: + static constexpr const char *TIME = "time"; + static constexpr const char *KVSTORE_TYPE = "kvStoreType"; +}; + +struct KvStoreMetaData { + static constexpr const char *APP_ID = "appId"; + static constexpr const char *BUNDLE_NAME = "bundleName"; + using json = nlohmann::json; + std::string appId = ""; + std::string appType = ""; + std::string bundleName = ""; + std::string dataDir = ""; + std::string deviceAccountId = ""; + std::string deviceId = ""; + bool isAutoSync = false; + bool isBackup = false; + bool isEncrypt = false; + KvStoreType kvStoreType = KvStoreType::DEVICE_COLLABORATION; + std::string schema = ""; + std::string storeId = ""; + std::string userId = ""; + std::int32_t uid = -1; + std::uint32_t version = 0; + int securityLevel = 0; + bool isDirty = false; + std::string Marshal() const; + void Unmarshal(const json &jObject); + + static bool CheckChiefValues(const json &jObject); + + static inline std::string GetAppId(const json &jObject) + { + return Serializable::GetVal(jObject, APP_ID, json::value_t::string, ""); + } + + static inline std::string GetBundleName(const json &jObject) + { + return Serializable::GetVal(jObject, BUNDLE_NAME, json::value_t::string, ""); + } + static inline std::string GetStoreId(const json &jObject) + { + return Serializable::GetVal(jObject, STORE_ID, json::value_t::string, ""); + } +private: + static constexpr const char *KVSTORE_TYPE = "kvStoreType"; + static constexpr const char *DEVICE_ID = "deviceId"; + static constexpr const char *USER_ID = "userId"; + static constexpr const char *STORE_ID = "storeId"; + static constexpr const char *ENCRYPT = "isEncrypt"; + static constexpr const char *BACKUP = "isBackup"; + static constexpr const char *AUTO_SYNC = "isAutoSync"; + static constexpr const char *SCHEMA = "schema"; + static constexpr const char *DATA_DIR = "dataDir"; + static constexpr const char *APP_TYPE = "appType"; + static constexpr const char *DEVICE_ACCOUNT_ID = "deviceAccountID"; + static constexpr const char *UID = "UID"; + static constexpr const char *VERSION = "version"; + static constexpr const char *SECURITY_LEVEL = "securityLevel"; + static constexpr const char *DIRTY_KEY = "isDirty"; +}; + + +struct MetaData { + std::int32_t kvStoreType; + KvStoreMetaData kvStoreMetaData; + SecretKeyMetaData secretKeyMetaData; + + static inline KvStoreType GetKvStoreType(const nlohmann::json &jObject) + { + return Serializable::GetVal(jObject, KVSTORE_TYPE, nlohmann::json::value_t::number_unsigned, + KvStoreType::INVALID_TYPE); + } +private: + static constexpr const char *KVSTORE_TYPE = "kvStoreType"; +}; + +struct DelegateGuard { + using Fn = std::function; + Fn action_; + DelegateGuard(Fn action) : action_(std::forward(action)) {} + + ~DelegateGuard() + { + if (action_) { + action_(); + } + } + DelegateGuard() = delete; + DelegateGuard(const DelegateGuard &) = delete; + DelegateGuard &operator=(const DelegateGuard &) = delete; +}; + +class KvStoreMetaManager { +public: + using NbDelegate = std::unique_ptr>; + + class MetaDeviceChangeListenerImpl : public AppDistributedKv::AppDeviceStatusChangeListener { + void OnDeviceChanged(const AppDistributedKv::DeviceInfo &info, + const AppDistributedKv::DeviceChangeType &type) const override; + + AppDistributedKv::ChangeLevelType GetChangeLevelType() const override; + }; + + ~KvStoreMetaManager(); + + static KvStoreMetaManager &GetInstance(); + + void InitMetaParameter(); + + void InitMetaListener(std::function observer); + + const NbDelegate &GetMetaKvStore(); + + Status CheckUpdateServiceMeta(const std::vector &metaKey, FLAG flag, const std::vector &val = {}); + + Status GenerateRootKey(); + + Status CheckRootKeyExist(); + + static std::vector GetMetaKey( + const std::string &deviceAccountId, const std::string &groupId, const std::string &bundleName, + const std::string &storeId, const std::string &key = ""); + + static std::string GetSecretKeyFile(const std::string &deviceAccountId, const std::string &appId, + const std::string &storeId); + + static std::string GetSecretSingleKeyFile(const std::string &deviceAccountId, const std::string &appId, + const std::string &storeId); + + Status GetSecretKeyFromMeta(const std::vector &metaSecretKey, + std::vector &key, bool &outdated); + + std::vector EncryptWorkKey(const std::vector &key); + + bool DecryptWorkKey(const std::vector &encryptedKey, std::vector &key); + + Status WriteSecretKeyToMeta(const std::vector &metaKey, const std::vector &key); + + Status WriteSecretKeyToFile(const std::string &secretKeyFile, const std::vector &key); + + Status + RemoveSecretKey(const std::string &deviceAccountId, const std::string &bundleName, const std::string &storeId); + + Status + RecoverSecretKeyFromFile(const std::string &secretKeyFile, const std::vector &metaSecretKey, + std::vector &key, bool &outdated); + + void ReKey(const std::string &deviceAccountId, const std::string &bundleName, const std::string &storeId, + sptr store); + + void ReKey(const std::string &deviceAccountId, const std::string &bundleName, const std::string &storeId, + sptr store); + + void GetStrategyMetaKey(const StrategyMeta ¶ms, std::string &retVal); + + Status DeleteStrategyMeta(const std::string &bundleName, const std::string &storeId); + + Status SaveStrategyMetaEnable(const std::string &key, bool enable); + + Status SaveStrategyMetaLabels(const std::string &key, + const std::vector &localLabels, + const std::vector &remoteSupportLabels); + + Status CheckSyncPermission(const std::string &userId, const std::string &appId, const std::string &storeId, + uint8_t flag, const std::string &deviceId); + + Status QueryKvStoreMetaDataByDeviceIdAndAppId(const std::string &devId, const std::string &appId, + KvStoreMetaData &val); + // json rule + void ToJson(nlohmann::json &j, const KvStoreMetaData &k); + + void FromJson(const nlohmann::json &j, KvStoreMetaData &k); + + Status GetKvStoreMeta(const std::vector &metaKey, KvStoreMetaData &kvStoreMetaData); + + bool GetKvStoreMetaDataByBundleName(const std::string &bundleName, KvStoreMetaData &metaData); + + bool GetKvStoreMetaDataByAppId(const std::string &appId, KvStoreMetaData &metaData); + + bool GetFullMetaData(std::map &entries); +private: + NbDelegate CreateMetaKvStore(); + + KvStoreMetaManager(); + + void InitMetaData(); + + void SubscribeMetaKvStore(); + + void SyncMeta(); + + void ConcatWithSharps(const std::vector ¶ms, std::string &retVal); + + Status GetStategyMeta(const std::string &key, std::map> &strategies); + + bool GetKvStoreMetaByType(const std::string &name, const std::string &val, KvStoreMetaData &metaData); + + class KvStoreMetaObserver : public DistributedDB::KvStoreObserver { + public: + virtual ~KvStoreMetaObserver(); + + // Database change callback + void OnChange(const DistributedDB::KvStoreChangedData &data) override; + std::function notify_ = nullptr; + }; + + static const inline std::string META_DB_APP_ID = "distributeddata"; + static constexpr const char *ROOT_KEY_ALIAS = "distributed_db_root_key"; + static constexpr const char *STRATEGY_META_PREFIX = "StrategyMetaData"; + static constexpr const char *CAPABILITY_ENABLED = "capabilityEnabled"; + static constexpr const char *CAPABILITY_RANGE = "capabilityRange"; + static constexpr const char *LOCAL_LABEL = "localLabel"; + static constexpr const char *REMOTE_LABEL = "remoteLabel"; + static constexpr const char *HARMONY_APP = "harmony"; + static constexpr int KEY_SIZE = 32; + static constexpr int HOURS_PER_YEAR = (24 * 365); + + NbDelegate metaDelegate_ {}; + std::string metaDBDirectory_; + DistributedDB::KvStoreDelegateManager kvStoreDelegateManager_; + static std::condition_variable cv_; + static std::mutex cvMutex_; + static MetaDeviceChangeListenerImpl listener_; + KvStoreMetaObserver metaObserver_; +}; +} // namespace DistributedKv +} // namespace OHOS +#endif // KVSTORE_META_MANAGER_H diff --git a/services/distributeddataservice/app/src/kvstore_observer_impl.cpp b/services/distributeddataservice/app/src/kvstore_observer_impl.cpp new file mode 100644 index 000000000..4ff122f74 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_observer_impl.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. + */ + +#define LOG_TAG "KvStoreObserverImpl" + +#include "kvstore_observer_impl.h" +#include +#include +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +using namespace std::chrono; + +KvStoreObserverImpl::KvStoreObserverImpl(SubscribeType subscribeType, sptr observerProxy) + : subscribeType_(subscribeType), observerProxy_(std::move(observerProxy)) +{ + ZLOGI("construct"); +} + +KvStoreObserverImpl::~KvStoreObserverImpl() +{ + ZLOGI("destruct"); +} + +// Data change callback +void KvStoreObserverImpl::OnChange(const DistributedDB::KvStoreChangedData &data) +{ + ZLOGI("onchange"); + if (observerProxy_ == nullptr) { + ZLOGE("observerProxy_ is nullptr."); + return; + } + std::list insertList = data.GetEntriesInserted(); + std::list updateList = data.GetEntriesUpdated(); + std::list deletedList = data.GetEntriesDeleted(); + + std::list insertListTmp; + std::list updateListTmp; + std::list deletedListTmp; + + for (const auto &entry : insertList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + insertListTmp.push_back(tmpEntry); + } + + for (const auto &entry : updateList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + updateListTmp.push_back(tmpEntry); + } + + for (const auto &entry : deletedList) { + Key key(entry.key); + Value value(entry.value); + Entry tmpEntry; + tmpEntry.key = key; + tmpEntry.value = value; + deletedListTmp.push_back(tmpEntry); + } + + ChangeNotification changeNotification(insertListTmp, updateListTmp, deletedListTmp, std::string(), false); + ZLOGI("call proxy OnChange"); + observerProxy_->OnChange(changeNotification, nullptr); +} + +SubscribeType KvStoreObserverImpl::GetSubscribeType() const +{ + return subscribeType_; +} + +sptr KvStoreObserverImpl::GetKvStoreObserverProxy() const +{ + return observerProxy_; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_observer_impl.h b/services/distributeddataservice/app/src/kvstore_observer_impl.h new file mode 100644 index 000000000..7887c2c42 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_observer_impl.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 KVSTORE_OBSERVER_IMPL_H +#define KVSTORE_OBSERVER_IMPL_H + +#include "ikvstore.h" +#include "ikvstore_observer.h" +#include "kv_store_delegate.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreObserverImpl : public DistributedDB::KvStoreObserver { +public: + KvStoreObserverImpl(SubscribeType subscribeType, sptr observerProxy); + + virtual ~KvStoreObserverImpl(); + + // Database change callback + void OnChange(const DistributedDB::KvStoreChangedData &data) override; + + SubscribeType GetSubscribeType() const; + sptr GetKvStoreObserverProxy() const; + +private: + SubscribeType subscribeType_; + sptr observerProxy_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_OBSERVER_IMPL_H diff --git a/services/distributeddataservice/app/src/kvstore_resultset_impl.cpp b/services/distributeddataservice/app/src/kvstore_resultset_impl.cpp new file mode 100755 index 000000000..dac050779 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_resultset_impl.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreResultsetImpl" + +#include "kvstore_resultset_impl.h" +#include +#include "dds_trace.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +constexpr int KvStoreResultSetImpl::INIT_POSTION; +KvStoreResultSetImpl::~KvStoreResultSetImpl() +{ +} + +KvStoreResultSetImpl::KvStoreResultSetImpl(DistributedDB::KvStoreResultSet *resultSet) + : kvStoreResultSet_(resultSet) +{ +} + +KvStoreResultSetImpl::KvStoreResultSetImpl(DistributedDB::Key keyPrefix, DistributedDB::KvStoreResultSet *resultSet) + : keyPrefix_(std::move(keyPrefix)), kvStoreResultSet_(resultSet) +{ +} + +int KvStoreResultSetImpl::GetCount() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return 0; + } + return kvStoreResultSet_->GetCount(); +} + +int KvStoreResultSetImpl::GetPosition() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return INIT_POSTION; + } + return kvStoreResultSet_->GetPosition(); +} + +bool KvStoreResultSetImpl::MoveToFirst() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->MoveToFirst(); +} + +bool KvStoreResultSetImpl::MoveToLast() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->MoveToLast(); +} + +bool KvStoreResultSetImpl::MoveToNext() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->MoveToNext(); +} + +bool KvStoreResultSetImpl::MoveToPrevious() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->MoveToPrevious(); +} + +bool KvStoreResultSetImpl::Move(int offset) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->Move(offset); +} + +bool KvStoreResultSetImpl::MoveToPosition(int position) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->MoveToPosition(position); +} + +bool KvStoreResultSetImpl::IsFirst() +{ + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->IsFirst(); +} + +bool KvStoreResultSetImpl::IsLast() +{ + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->IsLast(); +} + +bool KvStoreResultSetImpl::IsBeforeFirst() +{ + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->IsBeforeFirst(); +} + +bool KvStoreResultSetImpl::IsAfterLast() +{ + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return false; + } + return kvStoreResultSet_->IsAfterLast(); +} + +Status KvStoreResultSetImpl::GetEntry(Entry &entry) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(this->mutex_); + if (kvStoreResultSet_ == nullptr) { + return Status::ERROR; + } + if (GetCount() == 0) { + return Status::KEY_NOT_FOUND; + } + DistributedDB::Entry dbEntry; + DistributedDB::DBStatus dbStatus = kvStoreResultSet_->GetEntry(dbEntry); + if (dbStatus == DistributedDB::DBStatus::OK) { + Key tmpKey(dbEntry.key); + Value tmpValue(dbEntry.value); + entry.key = tmpKey; + entry.value = tmpValue; + return Status::SUCCESS; + } + return Status::KEY_NOT_FOUND; +} + +Status KvStoreResultSetImpl::CloseResultSet(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) +{ + if (kvStoreNbDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + std::shared_lock lock(this->mutex_); + DistributedDB::DBStatus status = kvStoreNbDelegate->CloseResultSet(kvStoreResultSet_); + if (status != DistributedDB::DBStatus::OK) { + return Status::DB_ERROR; + } + kvStoreResultSet_ = nullptr; + return Status::SUCCESS; +} + +Status KvStoreResultSetImpl::MigrateKvStore(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) +{ + if (kvStoreNbDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + + std::unique_lock lock(this->mutex_); + int position = GetPosition(); + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate->CloseResultSet(kvStoreResultSet_); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("close result set failed."); + return Status::DB_ERROR; + } + + DistributedDB::KvStoreResultSet *dbResultSet = nullptr; + dbStatus = kvStoreNbDelegate->GetEntries(keyPrefix_, dbResultSet); + if (dbStatus != DistributedDB::DBStatus::OK || dbResultSet == nullptr) { + ZLOGE("rebuild result set failed during get entries by key prefix."); + kvStoreResultSet_ = nullptr; + return Status::DB_ERROR; + } + kvStoreResultSet_ = dbResultSet; + kvStoreResultSet_->MoveToPosition(position); + + return Status::SUCCESS; +} +} // namespace OHOS::DistributedKv \ No newline at end of file diff --git a/services/distributeddataservice/app/src/kvstore_resultset_impl.h b/services/distributeddataservice/app/src/kvstore_resultset_impl.h new file mode 100755 index 000000000..b0880f932 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_resultset_impl.h @@ -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. + */ + +#ifndef KVSTORE_RESULTSET_IMPL_H +#define KVSTORE_RESULTSET_IMPL_H + +#include +#include +#include +#include "ikvstore_resultset.h" +#include "kv_store_result_set.h" +#include "kv_store_nb_delegate.h" + +namespace OHOS::DistributedKv { +class KvStoreResultSetImpl : public KvStoreResultSetStub { +public: + explicit KvStoreResultSetImpl(DistributedDB::KvStoreResultSet *resultSet); + KvStoreResultSetImpl(DistributedDB::Key keyPrefix, DistributedDB::KvStoreResultSet *resultSet); + ~KvStoreResultSetImpl() override; + + int GetCount() override; + + int GetPosition() override; + + bool MoveToFirst() override; + + bool MoveToLast() override; + + bool MoveToNext() override; + + bool MoveToPrevious() override; + + bool Move(int offset) override; + + bool MoveToPosition(int position) override; + + bool IsFirst() override; + + bool IsLast() override; + + bool IsBeforeFirst() override; + + bool IsAfterLast() override; + + Status GetEntry(Entry &entry) override; + + Status CloseResultSet(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + + Status MigrateKvStore(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); +private: + static constexpr int INIT_POSTION = -1; + + DistributedDB::Key keyPrefix_ {}; + mutable std::shared_mutex mutex_ {}; + DistributedDB::KvStoreResultSet *kvStoreResultSet_ = nullptr; +}; +} // namespace OHOS::DistributedKv +#endif // KVSTORE_RESULTSET_IMPL_H diff --git a/services/distributeddataservice/app/src/kvstore_snapshot_impl.cpp b/services/distributeddataservice/app/src/kvstore_snapshot_impl.cpp new file mode 100755 index 000000000..783a0c0a0 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_snapshot_impl.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreSnapshotImpl" + +#include "kvstore_snapshot_impl.h" +#include "constant.h" +#include "log_print.h" +#include "dds_trace.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreSnapshotImpl::KvStoreSnapshotImpl(DistributedDB::KvStoreSnapshotDelegate *kvStoreSnapshotDelegate, + KvStoreObserverImpl *kvStoreObserverImpl) + : kvStoreSnapshotDelegate_(kvStoreSnapshotDelegate), kvStoreObserverImpl_(kvStoreObserverImpl) +{ + ZLOGI("construct"); +} + +KvStoreSnapshotImpl::~KvStoreSnapshotImpl() +{ + ZLOGI("destruct"); + if (kvStoreObserverImpl_ != nullptr) { + delete kvStoreObserverImpl_; + } +} + +Status KvStoreSnapshotImpl::Get(const Key &key, Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::vector keyData = Constant::TrimCopy>(key.Data()); + + if (keyData.empty() || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(snapshotDelegateMutex_); + if (kvStoreSnapshotDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + + DistributedDB::Value retValue; + DistributedDB::DBStatus retValueStatus; + + auto valueCallbackFunction = [&](DistributedDB::DBStatus status, DistributedDB::Value valueTmp) { + retValueStatus = status; + retValue = valueTmp; + }; + + DistributedDB::Key dbKey = keyData; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreSnapshotDelegate_->Get(dbKey, valueCallbackFunction); + } + if (retValueStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error: %d.", static_cast(retValueStatus)); + if (retValueStatus == DistributedDB::DBStatus::NOT_FOUND) { + return Status::KEY_NOT_FOUND; + } + return Status::DB_ERROR; + } + + Value valueOut(retValue); + value = valueOut; + return Status::SUCCESS; +} + +void KvStoreSnapshotImpl::GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::vector retEntries; + Key trimmedPrefix = Key(Constant::TrimCopy>(prefixKey.Data())); + Key trimmedNext = Key(Constant::TrimCopy>(nextKey.Data())); + // handling parameter errors + if (trimmedPrefix.Size() > Constant::MAX_KEY_LENGTH || trimmedNext.Size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + callback(Status::INVALID_ARGUMENT, retEntries, nextKey); + return; + } + + std::shared_lock lock(snapshotDelegateMutex_); + if (kvStoreSnapshotDelegate_ == nullptr) { + ZLOGE("delegate is null."); + callback(Status::DB_ERROR, retEntries, nextKey); + return; + } + // search the buffer to find if this search has already been buffered. + std::lock_guard entryLock(entriesMutex_); + auto restPair = batchEntries_.begin(); + for (; restPair != batchEntries_.end(); restPair++) { + // firstly compare prefixKey + if (restPair->first == trimmedPrefix.ToString()) { + // secondly compare nextKey + if (restPair->second.front().key.ToString() == trimmedNext.ToString()) { + break; + } + } + } + + if (restPair != batchEntries_.end()) { + // buffer of this search has been found. read and remove returned entries from buffer. + auto &restList = restPair->second; + size_t retSize = 0; + // compute if add next entry to retEntries will let retEntries size exceeds IPC limit. + while (restList.size() > 0 && restList.front().value.Size() + retSize < SOFT_LIMIT) { + retSize += restList.front().key.Size() + IPC_WRITE_AMPLIFICATION + + restList.front().value.Size() + IPC_WRITE_AMPLIFICATION; + retEntries.push_back(restList.front()); + restList.pop_front(); + } + if (restList.size() > 0 && retEntries.size() > 0) { + callback(Status::SUCCESS, retEntries, restList.front().key); + return; + } + batchEntries_.erase(restPair); + if (restList.size() == 0) { + callback(Status::SUCCESS, retEntries, Key("")); + } else { + callback(Status::ILLEGAL_STATE, retEntries, Key("")); + } + return; + } + // holding entriesMutex until GetEntriesFromDelegate() finish. GetEntriesFromDelegate will change batchEntries_ too. + GetEntriesFromDelegateLocked(trimmedPrefix, trimmedNext, callback); +} + +void KvStoreSnapshotImpl::GetEntriesFromDelegateLocked(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::vector retEntries; + DistributedDB::DBStatus retValueStatus; + size_t retSize = 0; + Key nextStart; + // the returned entries can be separated into three part: + // part1: the already returned part. this part should be discarded. + // part2: entries to be returned this time. size of this part depends on ipc limit. + // part3: entries that cannot be returned due to ipc limit. part3 should be buffered to batchEntries_. + auto valueCallbackFunction = [&](DistributedDB::DBStatus status, const std::vector &entries) { + ZLOGD("delegate return entry size: %zu", entries.size()); + retValueStatus = status; + auto entry = entries.begin(); + // deal with part1: skip already returned entries. + if (nextKey.Size() != 0) { + while (!(Key(entry->key) == nextKey)) { + entry++; + } + if (entry == entries.end()) { + ZLOGE("search reach end before find nextkey"); + return; + } + } + // deal with part2: put entries to retEntries until put next entry will cause retEntries exceeds its size limit. + for (; entry != entries.end() && entry->value.size() + retSize < SOFT_LIMIT; entry++) { + Entry entryTmp; + entryTmp.key = Key(entry->key); + entryTmp.value = Value(entry->value); + retEntries.push_back(entryTmp); + retSize += entryTmp.value.Size() + IPC_WRITE_AMPLIFICATION + entryTmp.key.Size() + IPC_WRITE_AMPLIFICATION; + } + // all returned entries has been put to retEntries so there will not be a part 3. + if (entry == entries.end() || retEntries.size() == 0) { + nextStart = ""; + return; + } + // deal with part3: + if (batchEntries_.size() >= BUFFER_SIZE) { + // buffer is full. firstly remove the oldest buffer(the last element in batchEntries_). + batchEntries_.pop_back(); + } + nextStart = entry->key; + // secondly move the rest entries to buffer + std::list buffer; + for (; entry != entries.end(); entry++) { + Entry entryTmp; + entryTmp.key = Key(entry->key); + entryTmp.value = Value(entry->value); + buffer.push_back(entryTmp); + } + // thirdly put buffer to the front of batchEntries_, use prefixKey as its key + batchEntries_.push_front(std::make_pair(prefixKey.ToString(), std::move(buffer))); + }; // end of valueCallbackFunction + + std::shared_lock lock(snapshotDelegateMutex_); + DistributedDB::Key dbKey = prefixKey.Data(); + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreSnapshotDelegate_->GetEntries(dbKey, valueCallbackFunction); + } + if (retValueStatus == DistributedDB::DBStatus::NOT_FOUND) { + callback(Status::KEY_NOT_FOUND, retEntries, nextStart); + return; + } + if (retValueStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error: %d.", static_cast(retValueStatus)); + callback(Status::DB_ERROR, retEntries, nextStart); + return; + } + ZLOGD("retEntries size: %zu : %zu.", retEntries.size(), retSize); + callback(Status::SUCCESS, retEntries, nextStart); +} + +void KvStoreSnapshotImpl::GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + std::vector retKeys; + Key trimmedPrefix = Key(Constant::TrimCopy>(prefixKey.Data())); + Key trimmedNext = Key(Constant::TrimCopy>(nextKey.Data())); + if (trimmedPrefix.Size() > Constant::MAX_KEY_LENGTH || trimmedNext.Size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + callback(Status::INVALID_ARGUMENT, retKeys, nextKey); + return; + } + + std::shared_lock lock(snapshotDelegateMutex_); + if (kvStoreSnapshotDelegate_ == nullptr) { + ZLOGE("delegate is null."); + callback(Status::DB_ERROR, retKeys, nextKey); + return; + } + + std::lock_guard keyLock(keysMutex_); + // search the buffer to find if this search has been buffered + auto restPair = batchKeys_.begin(); + for (; restPair != batchKeys_.end(); restPair++) { + if (restPair->first == trimmedPrefix.ToString()) { + if (restPair->second.front() == trimmedNext) { + break; + } + } + } + + // buffer of this search has been found + if (restPair != batchKeys_.end()) { + auto restList = restPair->second; + size_t retSize = 0; + while (restList.size() > 0 && retSize < SOFT_LIMIT) { + retSize += restList.front().Size() + IPC_WRITE_AMPLIFICATION; + retKeys.push_back(restList.front()); + restList.pop_front(); + } + if (restList.size() > 0) { + callback(Status::SUCCESS, retKeys, restList.front()); + } else { + callback(Status::SUCCESS, retKeys, Key("")); + batchKeys_.erase(restPair); + } + return; + } + + // holding keysMutex until GetKeysFromDelegate() finish. GetKeysFromDelegate will change batchkeys_ too. + GetKeysFromDelegateLocked(trimmedPrefix, trimmedNext, callback); +} + +void KvStoreSnapshotImpl::GetKeysFromDelegateLocked(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + // just the same as GetEntriesFromDelegate + std::vector retKeys; + DistributedDB::DBStatus retValueStatus; + size_t retSize = 0; + Key nextStart; + auto valueCallbackFunction = [&](DistributedDB::DBStatus status, const std::vector &entries) { + ZLOGD("delegate return entry size: %zu", entries.size()); + retValueStatus = status; + auto entry = entries.begin(); + if (nextKey.Size() != 0) { + while (!(Key(entry->key) == nextKey)) { + entry++; + } + } + for (; entry != entries.end() && entry->key.size() + retSize < SOFT_LIMIT; entry++) { + retKeys.push_back(Key(entry->key)); + retSize += entry->key.size() + IPC_WRITE_AMPLIFICATION; + } + + if (entry == entries.end()) { + nextStart = ""; + return; + } + if (batchKeys_.size() >= BUFFER_SIZE) { + batchKeys_.pop_back(); + } + std::list buffer; + nextStart = Key(entry->key); + for (; entry != entries.end(); entry++) { + buffer.push_back(Key(entry->key)); + } + batchKeys_.push_front(std::make_pair(prefixKey.ToString(), std::move(buffer))); + }; + + std::shared_lock lock(snapshotDelegateMutex_); + DistributedDB::Key dbKey = prefixKey.Data(); + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + kvStoreSnapshotDelegate_->GetEntries(dbKey, valueCallbackFunction); + } + if (retValueStatus == DistributedDB::DBStatus::NOT_FOUND) { + callback(Status::KEY_NOT_FOUND, retKeys, nextStart); + return; + } + if (retValueStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error: %d.", static_cast(retValueStatus)); + callback(Status::DB_ERROR, retKeys, nextStart); + return; + } + ZLOGD("retKeys size: %zu : %zu.", retKeys.size(), retSize); + callback(Status::SUCCESS, retKeys, nextStart); +} + +Status KvStoreSnapshotImpl::Release(DistributedDB::KvStoreDelegate *kvStoreDelegate) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGI("Releasing KvStoreSnapshot."); + if (kvStoreDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(snapshotDelegateMutex_); + DistributedDB::DBStatus status = kvStoreDelegate->ReleaseKvStoreSnapshot(kvStoreSnapshotDelegate_); + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("Error occurs during Releasing KvStoreSnapshot, error code %d.", status); + return Status::DB_ERROR; + } + kvStoreSnapshotDelegate_ = nullptr; + return Status::SUCCESS; +} + +Status KvStoreSnapshotImpl::MigrateKvStore(DistributedDB::KvStoreDelegate *kvStoreDelegate) +{ + if (kvStoreDelegate == nullptr) { + return Status::INVALID_ARGUMENT; + } + ZLOGI("begin."); + DistributedDB::KvStoreSnapshotDelegate *snapshotDelegate = nullptr; + DistributedDB::DBStatus dbStatus; + auto snapshotCallbackFunction = [&](DistributedDB::DBStatus status, + DistributedDB::KvStoreSnapshotDelegate *snapshot) { + dbStatus = status; + snapshotDelegate = snapshot; + }; + std::unique_lock lock(snapshotDelegateMutex_); + kvStoreDelegate->GetKvStoreSnapshot(kvStoreObserverImpl_, snapshotCallbackFunction); + if (dbStatus != DistributedDB::DBStatus::OK || snapshotDelegate == nullptr) { + ZLOGE("delegate return nullptr or errcode, dbStatus:%d.", static_cast(dbStatus)); + return Status::DB_ERROR; + } + + kvStoreSnapshotDelegate_ = snapshotDelegate; + return Status::SUCCESS; +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_snapshot_impl.h b/services/distributeddataservice/app/src/kvstore_snapshot_impl.h new file mode 100644 index 000000000..d806f1a44 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_snapshot_impl.h @@ -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. + */ + +#ifndef KVSTORE_SNAPSHOT_IMPL_H +#define KVSTORE_SNAPSHOT_IMPL_H + +#include +#include +#include +#include +#include +#include +#include "kv_store_delegate.h" +#include "ikvstore_snapshot.h" +#include "kv_store_snapshot_delegate.h" +#include "types.h" +#include "kvstore_observer_impl.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreSnapshotImpl : public KvStoreSnapshotImplStub { +public: + explicit KvStoreSnapshotImpl(DistributedDB::KvStoreSnapshotDelegate *kvStoreSnapshotDelegate, + KvStoreObserverImpl *kvStoreObserverImpl); + + virtual ~KvStoreSnapshotImpl(); + + Status Get(const Key &key, Value &value) override; + + void GetEntries(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) override; + + void GetKeys(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback) override; + + Status Release(DistributedDB::KvStoreDelegate *kvStoreDelegate); + + Status MigrateKvStore(DistributedDB::KvStoreDelegate *kvStoreDelegate); + +private: + // distributeddb is responsible for free kvStoreSnapshotDelegate_, + // by calling ReleaseKvStoreSnapshot in kvstore, + // can not free it in KvStoreSnapshotImpl's destructor. + mutable std::shared_mutex snapshotDelegateMutex_{}; + DistributedDB::KvStoreSnapshotDelegate *kvStoreSnapshotDelegate_; + KvStoreObserverImpl *kvStoreObserverImpl_; + // write amplification of each write parcel operation. currently zero. + static constexpr unsigned int IPC_WRITE_AMPLIFICATION = 0; + + // max size of returned entries or keys. size of the key and IPC_WRITE_AMPLIFICATION of the last element is ignored. + // IPC limit is 819200 currently. SOFT_LIMIT should be smaller than IPC limit. + static constexpr unsigned int SOFT_LIMIT = 750000; + + // max size of batchEntries_ and batchKeys_ + static constexpr unsigned int BUFFER_SIZE = 3; + + // temporarily storage entries of an unfinished search. + std::list>> batchEntries_; + std::mutex entriesMutex_; + void GetEntriesFromDelegateLocked(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback); + + // temporarily storage keys of an unfinished search. + std::list>> batchKeys_; + std::mutex keysMutex_; + void GetKeysFromDelegateLocked(const Key &prefixKey, const Key &nextKey, + std::function &, const Key &)> callback); +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_SNAPSHOT_IMPL_H diff --git a/services/distributeddataservice/app/src/kvstore_sync_manager.cpp b/services/distributeddataservice/app/src/kvstore_sync_manager.cpp new file mode 100755 index 000000000..746477abe --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_sync_manager.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. + */ + +#define LOG_TAG "KvSyncManager" + +#include "kvstore_sync_manager.h" +#include "log_print.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreSyncManager::KvStoreSyncManager() : syncScheduler_() +{} + +KvStoreSyncManager::~KvStoreSyncManager() {} + +Status KvStoreSyncManager::AddSyncOperation(uintptr_t syncId, uint32_t delayMs, const SyncFunc &syncFunc, + const SyncEnd &syncEnd) +{ + if (syncId == 0 || syncFunc == nullptr) { + return Status::INVALID_ARGUMENT; + } + uint32_t opSeq = ++syncOpSeq_; + SyncEnd endFunc; + if (syncEnd != nullptr) { + endFunc = [opSeq, delayMs, syncEnd, this](const std::map &devices) { + RemoveSyncingOp(opSeq, (delayMs == 0) ? realtimeSyncingOps_ : delaySyncingOps_); + syncEnd(devices); + }; + } + + auto beginTime = std::chrono::system_clock::now() + std::chrono::milliseconds(delayMs); + KvSyncOperation syncOp{ syncId, opSeq, delayMs, syncFunc, endFunc, beginTime }; + if (delayMs == 0) { + if (endFunc != nullptr) { + std::lock_guard lock(syncOpsMutex_); + realtimeSyncingOps_.push_back(syncOp); + } + return syncFunc(endFunc); + } + + std::lock_guard lock(syncOpsMutex_); + scheduleSyncOps_.emplace(beginTime, syncOp); + ZLOGD("add op %u delay %u count %zu.", opSeq, delayMs, scheduleSyncOps_.size()); + if ((scheduleSyncOps_.size() == 1) || + (nextScheduleTime_ > beginTime + std::chrono::milliseconds(GetExpireTimeRange(delayMs)))) { + AddTimer(beginTime); + } + return Status::SUCCESS; +} + +uint32_t KvStoreSyncManager::GetExpireTimeRange(uint32_t delayMs) const +{ + uint32_t range = delayMs / DELAY_TIME_RANGE_DIVISOR; + return std::max(range, SYNC_MIN_DELAY_MS >> 1); +} + +Status KvStoreSyncManager::RemoveSyncOperation(uintptr_t syncId) +{ + auto pred = [syncId](const KvSyncOperation &op) -> bool { return syncId == op.syncId; }; + + std::lock_guard lock(syncOpsMutex_); + uint32_t count = DoRemoveSyncingOp(pred, realtimeSyncingOps_); + count += DoRemoveSyncingOp(pred, delaySyncingOps_); + + auto &syncOps = scheduleSyncOps_; + for (auto it = syncOps.begin(); it != syncOps.end();) { + if (pred(it->second)) { + count++; + it = syncOps.erase(it); + } else { + ++it; + } + } + return (count > 0) ? Status::SUCCESS : Status::ERROR; +} + +uint32_t KvStoreSyncManager::DoRemoveSyncingOp(OpPred pred, std::list &syncingOps) +{ + uint32_t count = 0; + for (auto it = syncingOps.begin(); it != syncingOps.end();) { + if (pred(*it)) { + count++; + it = syncingOps.erase(it); + } else { + ++it; + } + } + return count; +} + +Status KvStoreSyncManager::RemoveSyncingOp(uint32_t opSeq, std::list &syncingOps) +{ + auto pred = [opSeq](const KvSyncOperation &op) -> bool { return opSeq == op.opSeq; }; + + ZLOGD("remove op %u", opSeq); + std::lock_guard lock(syncOpsMutex_); + uint32_t count = DoRemoveSyncingOp(pred, syncingOps); + return (count == 1) ? Status::SUCCESS : Status::ERROR; +} + +void KvStoreSyncManager::AddTimer(const TimePoint &expireTime) +{ + ZLOGD("time %lld", expireTime.time_since_epoch().count()); + nextScheduleTime_ = expireTime; + syncScheduler_.At(expireTime, [time = expireTime, this]() { Schedule(time); }); +} + +bool KvStoreSyncManager::GetTimeoutSyncOps(const TimePoint ¤tTime, std::list &syncOps) +{ + std::lock_guard lock(syncOpsMutex_); + if ((!realtimeSyncingOps_.empty()) && (!scheduleSyncOps_.empty())) { + // the last processing time is less than priorSyncingTime + auto priorSyncingTime = std::chrono::milliseconds(REALTIME_PRIOR_SYNCING_MS); + if (currentTime < realtimeSyncingOps_.rbegin()->beginTime + priorSyncingTime) { + return true; + } + } + for (auto it = scheduleSyncOps_.begin(); it != scheduleSyncOps_.end();) { + const auto &expireTime = it->first; + const auto &op = it->second; + // currentTime is earlier than expireTime minus delayMs + if (currentTime + std::chrono::milliseconds(GetExpireTimeRange(op.delayMs)) < expireTime) { + break; + } + + syncOps.push_back(op); + if (op.syncEnd != nullptr) { + delaySyncingOps_.push_back(op); + } + it = scheduleSyncOps_.erase(it); + } + return false; +} + +void KvStoreSyncManager::DoCheckSyncingTimeout(std::list &syncingOps) +{ + auto syncingTimeoutPred = [](const KvSyncOperation &op) -> bool { + return op.beginTime + std::chrono::milliseconds(SYNCING_TIMEOUT_MS) < std::chrono::system_clock::now(); + }; + + uint32_t count = DoRemoveSyncingOp(syncingTimeoutPred, syncingOps); + if (count > 0) { + ZLOGI("remove %u syncing ops by timeout", count); + } +} + +void KvStoreSyncManager::Schedule(const TimePoint &time) +{ + ZLOGD("timeout %lld", time.time_since_epoch().count()); + std::list syncOps; + bool delaySchedule = GetTimeoutSyncOps(time, syncOps); + + for (const auto &op : syncOps) { + op.syncFunc(op.syncEnd); + } + + std::lock_guard lock(syncOpsMutex_); + DoCheckSyncingTimeout(realtimeSyncingOps_); + DoCheckSyncingTimeout(delaySyncingOps_); + if (!scheduleSyncOps_.empty()) { + auto nextTime = scheduleSyncOps_.begin()->first; + if (delaySchedule) { + nextTime = std::chrono::system_clock::now() + std::chrono::milliseconds(SYNC_MIN_DELAY_MS); + } + AddTimer(nextTime); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_sync_manager.h b/services/distributeddataservice/app/src/kvstore_sync_manager.h new file mode 100755 index 000000000..24eab7f30 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_sync_manager.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 KVSTORE_SYNC_MANAGER_H +#define KVSTORE_SYNC_MANAGER_H + +#include +#include +#include "types.h" +#include "kv_scheduler.h" +#include "kv_store_nb_delegate.h" + +namespace OHOS { +namespace DistributedKv { +class KvStoreSyncManager { +public: + static constexpr uint32_t SYNC_DEFAULT_DELAY_MS = 1000; + static constexpr uint32_t SYNC_MIN_DELAY_MS = 100; + static constexpr uint32_t SYNC_MAX_DELAY_MS = 1000 * 3600 * 24; // 24hours + static constexpr uint32_t SYNC_RETRY_MAX_COUNT = 3; + static KvStoreSyncManager *GetInstance() + { + static KvStoreSyncManager syncManager; + return &syncManager; + } + using TimePoint = std::chrono::system_clock::time_point; + using SyncEnd = std::function &)>; + using SyncFunc = std::function; + + struct KvSyncOperation { + uintptr_t syncId{ 0 }; + uint32_t opSeq{ 0 }; + uint32_t delayMs{ 0 }; + SyncFunc syncFunc{}; + SyncEnd syncEnd{}; + TimePoint beginTime{}; + }; + using OpPred = std::function; + Status AddSyncOperation(uintptr_t syncId, uint32_t delayMs, const SyncFunc &syncFunc, + const SyncEnd &syncEnd); + Status RemoveSyncOperation(uintptr_t syncId); +private: + KvStoreSyncManager(); + ~KvStoreSyncManager(); + + uint32_t GetExpireTimeRange(uint32_t delayMs) const; + uint32_t DoRemoveSyncingOp(OpPred pred, std::list &syncingOps); + Status RemoveSyncingOp(uint32_t opSeq, std::list &syncingOps); + void AddTimer(const TimePoint &expireTime); + bool GetTimeoutSyncOps(const TimePoint &time, std::list &syncOps); + void DoCheckSyncingTimeout(std::list &syncingOps); + void Schedule(const TimePoint &expireTime); + + static constexpr uint32_t SYNCING_TIMEOUT_MS = 5000; + static constexpr uint32_t REALTIME_PRIOR_SYNCING_MS = 300; + static constexpr uint32_t DELAY_TIME_RANGE_DIVISOR = 4; + + mutable std::mutex syncOpsMutex_{}; + std::list realtimeSyncingOps_{}; + std::list delaySyncingOps_{}; + std::multimap scheduleSyncOps_{}; + + KvScheduler syncScheduler_{}; + TimePoint nextScheduleTime_{}; + std::atomic_uint32_t syncOpSeq_{ 0 }; +}; +} // namespace DistributedKv +} // namespace OHOS + +#endif // KVSTORE_SYNC_MANAGER_H diff --git a/services/distributeddataservice/app/src/kvstore_user_manager.cpp b/services/distributeddataservice/app/src/kvstore_user_manager.cpp new file mode 100755 index 000000000..237431185 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_user_manager.cpp @@ -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. + */ + +#define LOG_TAG "KvStoreUserManager" + +#include "kvstore_user_manager.h" +#include "account_delegate.h" +#include "constant.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "permission_validator.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreUserManager::KvStoreUserManager(const std::string &deviceAccountId) + : appMutex_(), + appMap_(), + deviceAccountId_(deviceAccountId) +{ + ZLOGI("begin."); +} + +KvStoreUserManager::~KvStoreUserManager() +{ + ZLOGI("begin."); + appMap_.clear(); +} + +Status KvStoreUserManager::GetKvStore(const Options &options, const std::string &appId, const std::string &storeId, + const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(appId); + if (it != appMap_.end()) { + return (it->second).GetKvStore(options, storeId, cipherKey, callback); + } + + auto result = appMap_.emplace(std::piecewise_construct, std::forward_as_tuple(appId), + std::forward_as_tuple(appId, deviceAccountId_)); + if (result.second && result.first != appMap_.end()) { + return (result.first->second).GetKvStore(options, storeId, cipherKey, callback); + } else { + ZLOGE("emplace failed."); + callback(nullptr); + return Status::ERROR; + } +} + +Status KvStoreUserManager::GetSingleKvStore(const Options &options, const std::string &appId, + const std::string &storeId, const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(appId); + if (it != appMap_.end()) { + return (it->second).GetKvStore(options, storeId, cipherKey, callback); + } + auto result = appMap_.emplace(std::piecewise_construct, std::forward_as_tuple(appId), + std::forward_as_tuple(appId, deviceAccountId_)); + if (result.second && result.first != appMap_.end()) { + return (result.first->second).GetKvStore(options, storeId, cipherKey, callback); + } else { + ZLOGE("emplace failed."); + callback(nullptr); + return Status::ERROR; + } +} + +Status KvStoreUserManager::CloseKvStore(const std::string &appId, const std::string &storeId) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(appId); + if (it != appMap_.end()) { + return (it->second).CloseKvStore(storeId); + } + + ZLOGE("store not open."); + return Status::STORE_NOT_OPEN; +} + +Status KvStoreUserManager::CloseAllKvStore(const std::string &appId) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(appId); + if (it != appMap_.end()) { + return (it->second).CloseAllKvStore(); + } + + ZLOGE("store not open."); + return Status::STORE_NOT_OPEN; +} + +void KvStoreUserManager::CloseAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + for (auto &it : appMap_) { + (it.second).CloseAllKvStore(); + } +} + +Status KvStoreUserManager::DeleteKvStore(const std::string &bundleName, const std::string &storeId) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(bundleName); + if (it != appMap_.end()) { + auto status = (it->second).DeleteKvStore(storeId); + if ((it->second).GetTotalKvStoreNum() == 0) { + ZLOGI("There is not kvstore, so remove the app manager."); + appMap_.erase(it); + } + return status; + } + KvStoreAppManager kvStoreAppManager(bundleName, deviceAccountId_); + return kvStoreAppManager.DeleteKvStore(storeId); +} + +void KvStoreUserManager::DeleteAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + for (auto &it : appMap_) { + (it.second).DeleteAllKvStore(); + } + appMap_.clear(); +} + +// Migrate all KvStore DB delegate object when harmony account changed. +Status KvStoreUserManager::MigrateAllKvStore(const std::string &harmonyAccountId) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + Status status = Status::SUCCESS; + for (auto &it : appMap_) { + status = (it.second).MigrateAllKvStore(harmonyAccountId); + if (status != Status::SUCCESS) { + ZLOGE("migrate all kvstore for app-%s failed, status:%d.", + it.first.c_str(), static_cast(status)); + status = Status::MIGRATION_KVSTORE_FAILED; + } + } + return status; +} + +std::string KvStoreUserManager::GetDbDir(const std::string &bundleName, const Options &options) +{ + ZLOGI("begin."); + std::lock_guard lg(appMutex_); + auto it = appMap_.find(bundleName); + if (it != appMap_.end()) { + return (it->second).GetDbDir(options); + } + return ""; +} + +void KvStoreUserManager::Dump(int fd) const +{ + const std::string prefix(4, ' '); + dprintf(fd, "%s--------------------------------------------------------------\n", prefix.c_str()); + dprintf(fd, "%sUserID : %s\n", prefix.c_str(), KvStoreUtils::GetAppIdByBundleName(userId_).c_str()); + dprintf(fd, "%sApp count : %u\n", prefix.c_str(), static_cast(appMap_.size())); + for (const auto &pair : appMap_) { + pair.second.Dump(fd); + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/src/kvstore_user_manager.h b/services/distributeddataservice/app/src/kvstore_user_manager.h new file mode 100755 index 000000000..5ef3a1d41 --- /dev/null +++ b/services/distributeddataservice/app/src/kvstore_user_manager.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 KV_STORE_USER_MANAGER_H +#define KV_STORE_USER_MANAGER_H + +#include +#include +#include "kvstore_app_manager.h" +#include "kvstore_impl.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { + +class KvStoreUserManager { +public: + explicit KvStoreUserManager(const std::string &deviceAccountId); + + virtual ~KvStoreUserManager(); + + Status GetKvStore(const Options &options, const std::string &appId, const std::string &storeId, + const std::vector &cipherKey, std::function)> callback); + + Status GetSingleKvStore(const Options &options, const std::string &appId, const std::string &storeId, + const std::vector &cipherKey, std::function)> callback); + + Status CloseKvStore(const std::string &appId, const std::string &storeId); + + Status CloseAllKvStore(const std::string &appId); + + void CloseAllKvStore(); + + Status DeleteKvStore(const std::string &bundleName, const std::string &storeId); + + void DeleteAllKvStore(); + + Status MigrateAllKvStore(const std::string &harmonyAccountId); + + std::string GetDbDir(const std::string &bundleName, const Options &options); + + void Dump(int fd) const; + +private: + std::mutex appMutex_; + std::map appMap_; + std::string deviceAccountId_; + std::string userId_; +}; + +} // namespace DistributedKv +} // namespace OHOS + +#endif // KV_STORE_USER_MANAGER_H diff --git a/services/distributeddataservice/app/src/query_helper.cpp b/services/distributeddataservice/app/src/query_helper.cpp new file mode 100644 index 000000000..fa0494240 --- /dev/null +++ b/services/distributeddataservice/app/src/query_helper.cpp @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "QueryHelper" + +#include "query_helper.h" +#include +#include +#include +#include "kvstore_utils.h" +#include "data_query.h" +#include "log_print.h" + +namespace OHOS::DistributedKv { +constexpr int QUERY_SKIP_SIZE = 1; +constexpr int QUERY_WORD_SIZE = 2; +constexpr int MAX_QUERY_LENGTH = 5 * 1024; // Max query string length 5k +constexpr int MAX_QUERY_COMPLEXITY = 500; // Max query complexity 500 +bool QueryHelper::hasPrefixKey_{}; +std::string QueryHelper::deviceId_{}; + +DistributedDB::Query QueryHelper::StringToDbQuery(const std::string &query, bool &isSuccess) +{ + ZLOGI("query string length:%zu", query.length()); + DistributedDB::Query dbQuery = DistributedDB::Query::Select(); + if (query.size() == 0) { + ZLOGI("Query string is empty."); + isSuccess = true; + return dbQuery; + } + if (query.size() > MAX_QUERY_LENGTH) { + ZLOGE("Query string is too long."); + isSuccess = false; + return dbQuery; + } + deviceId_.clear(); + hasPrefixKey_ = (query.find(DataQuery::KEY_PREFIX) != std::string::npos); + size_t pos = query.find_first_not_of(DataQuery::SPACE); + std::string inputTrim = (pos == std::string::npos) ? "" : query.substr(pos); + std::regex regex(" "); + std::vector words( + std::sregex_token_iterator(inputTrim.begin(), inputTrim.end(), regex, -1), // regex split string by space + std::sregex_token_iterator() + ); + + int pointer = 0; // Read pointer starts at 0 + int end = words.size() - 1; // Read pointer ends at size - 1 + int count = 0; // Counts how many keywords has been handled + while (pointer <= end && count <= MAX_QUERY_COMPLEXITY) { + count++; + std::string keyword = words.at(pointer); + if (keyword == DataQuery::EQUAL_TO) { + HandleEqualTo(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::NOT_EQUAL_TO) { + HandleNotEqualTo(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::GREATER_THAN) { + HandleGreaterThan(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::LESS_THAN) { + HandleLessThan(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::GREATER_THAN_OR_EQUAL_TO) { + HandleGreaterThanOrEqualTo(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::LESS_THAN_OR_EQUAL_TO) { + HandleLessThanOrEqualTo(words, pointer, end, isSuccess, dbQuery); + } else { + Handle(words, pointer, end, isSuccess, dbQuery); + } + if (!isSuccess) { + ZLOGE("Invalid params."); + return DistributedDB::Query::Select(); + } + } + return dbQuery; +} + +void QueryHelper::Handle(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + std::string keyword = words.at(pointer); + if (keyword == DataQuery::IS_NULL) { + HandleIsNull(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::IN) { + HandleIn(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::NOT_IN) { + HandleNotIn(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::LIKE) { + HandleLike(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::NOT_LIKE) { + HandleNotLike(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::AND) { + HandleAnd(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::OR) { + HandleOr(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::ORDER_BY_ASC) { + HandleOrderByAsc(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::ORDER_BY_DESC) { + HandleOrderByDesc(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::LIMIT) { + HandleLimit(words, pointer, end, isSuccess, dbQuery); + } else { + HandleExtra(words, pointer, end, isSuccess, dbQuery); + } +} + +void QueryHelper::HandleExtra(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + std::string keyword = words.at(pointer); + if (keyword == DataQuery::BEGIN_GROUP) { + HandleBeginGroup(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::END_GROUP) { + HandleEndGroup(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::KEY_PREFIX) { + HandleKeyPrefix(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::IS_NOT_NULL) { + HandleIsNotNull(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::DEVICE_ID) { + HandleDeviceId(words, pointer, end, isSuccess, dbQuery); + } else if (keyword == DataQuery::SUGGEST_INDEX) { + HandleSetSuggestIndex(words, pointer, end, isSuccess, dbQuery); + } else { + ZLOGE("Invalid keyword."); + isSuccess = false; + } +} + +void QueryHelper::HandleEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("EqualTo not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.EqualTo(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.EqualTo(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.EqualTo(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_BOOLEAN) { + dbQuery.EqualTo(StringToString(fieldName), StringToBoolean(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.EqualTo(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("EqualTo wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleNotEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("NotEqualTo not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.NotEqualTo(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.NotEqualTo(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.NotEqualTo(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_BOOLEAN) { + dbQuery.NotEqualTo(StringToString(fieldName), StringToBoolean(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.NotEqualTo(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("NotEqualTo wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleGreaterThan(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("GreaterThan not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.GreaterThan(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.GreaterThan(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.GreaterThan(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.GreaterThan(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("GreaterThan wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleLessThan(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("LessThan not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.LessThan(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.LessThan(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.LessThan(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.LessThan(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("LessThan wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleGreaterThanOrEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("GreaterThanOrEqualTo not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.GreaterThanOrEqualTo(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.GreaterThanOrEqualTo(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.GreaterThanOrEqualTo(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.GreaterThanOrEqualTo(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("GreaterThanOrEqualTo wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleLessThanOrEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 3 > end) { // This keyword has 3 following params + ZLOGE("LessThanOrEqualTo not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + const std::string &fieldValue = words.at(pointer + 3); // fieldValue + if (fieldType == DataQuery::TYPE_INTEGER) { + dbQuery.LessThanOrEqualTo(StringToString(fieldName), StringToInt(fieldValue)); + } else if (fieldType == DataQuery::TYPE_LONG) { + dbQuery.LessThanOrEqualTo(StringToString(fieldName), StringToLong(fieldValue)); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + dbQuery.LessThanOrEqualTo(StringToString(fieldName), StringToDouble(fieldValue)); + } else if (fieldType == DataQuery::TYPE_STRING) { + dbQuery.LessThanOrEqualTo(StringToString(fieldName), StringToString(fieldValue)); + } else { + ZLOGE("LessThanOrEqualTo wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer += 4; // Pointer goes to next keyword +} + +void QueryHelper::HandleIsNull(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("IsNull not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + dbQuery.IsNull(StringToString(fieldName)); + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +void QueryHelper::HandleIsNotNull(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("IsNotNull not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + dbQuery.IsNotNull(StringToString(fieldName)); + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +void QueryHelper::HandleIn(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 4 > end || words.at(pointer + 3) != DataQuery::START_IN) { // This keyword has at least 4 params + ZLOGE("In not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + int elementPointer = pointer + 4; // first fieldValue, or END if list is empty + if (fieldType == DataQuery::TYPE_INTEGER) { + const std::vector intValueList = GetIntegerList(words, elementPointer, end); + dbQuery.In(StringToString(fieldName), intValueList); + } else if (fieldType == DataQuery::TYPE_LONG) { + const std::vector longValueList = GetLongList(words, elementPointer, end); + dbQuery.In(StringToString(fieldName), longValueList); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + const std::vector doubleValueList = GetDoubleList(words, elementPointer, end); + dbQuery.In(StringToString(fieldName), doubleValueList); + } else if (fieldType == DataQuery::TYPE_STRING) { + const std::vector stringValueList = GetStringList(words, elementPointer, end); + dbQuery.In(StringToString(fieldName), stringValueList); + } else { + ZLOGE("In wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer = elementPointer + 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleNotIn(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 4 > end || words.at(pointer + 3) != DataQuery::START_IN) { // This keyword has at least 4 params + ZLOGE("NotIn not enough params."); + isSuccess = false; + return; + } + const std::string &fieldType = words.at(pointer + 1); // fieldType + const std::string &fieldName = words.at(pointer + 2); // fieldName + int elementPointer = pointer + 4; // first fieldValue, or END if list is empty + if (fieldType == DataQuery::TYPE_INTEGER) { + const std::vector intValueList = GetIntegerList(words, elementPointer, end); + dbQuery.NotIn(StringToString(fieldName), intValueList); + } else if (fieldType == DataQuery::TYPE_LONG) { + const std::vector longValueList = GetLongList(words, elementPointer, end); + dbQuery.NotIn(StringToString(fieldName), longValueList); + } else if (fieldType == DataQuery::TYPE_DOUBLE) { + const std::vector doubleValueList = GetDoubleList(words, elementPointer, end); + dbQuery.NotIn(StringToString(fieldName), doubleValueList); + } else if (fieldType == DataQuery::TYPE_STRING) { + const std::vector stringValueList = GetStringList(words, elementPointer, end); + dbQuery.NotIn(StringToString(fieldName), stringValueList); + } else { + ZLOGE("NotIn wrong type."); + isSuccess = false; + return; + } + isSuccess = true; + pointer = elementPointer + 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleLike(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 2 > end) { // This keyword has 2 following params + ZLOGE("Like not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + const std::string &fieldValue = words.at(pointer + 2); // fieldValue + dbQuery.Like(StringToString(fieldName), StringToString(fieldValue)); + isSuccess = true; + pointer += 3; // Pointer goes to next keyword +} + +void QueryHelper::HandleNotLike(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 2 > end) { // This keyword has 2 following params + ZLOGE("NotLike not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + const std::string &fieldValue = words.at(pointer + 2); // fieldValue + dbQuery.NotLike(StringToString(fieldName), StringToString(fieldValue)); + isSuccess = true; + pointer += 3; // Pointer goes to next keyword +} + +void QueryHelper::HandleAnd(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + dbQuery.And(); + isSuccess = true; + pointer += 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleOr(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + dbQuery.Or(); + isSuccess = true; + pointer += 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleOrderByAsc(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("OrderByAsc not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + dbQuery.OrderBy(StringToString(fieldName), true); + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +void QueryHelper::HandleOrderByDesc(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("OrderByDesc not enough params."); + isSuccess = false; + return; + } + const std::string &fieldName = words.at(pointer + 1); // fieldName + dbQuery.OrderBy(StringToString(fieldName), false); + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +void QueryHelper::HandleLimit(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 2 > end) { // This keyword has 2 following params + ZLOGE("Limit not enough params."); + isSuccess = false; + return; + } + const int number = StringToInt(words.at(pointer + 1)); // number + const int offset = StringToInt(words.at(pointer + 2)); // offset + dbQuery.Limit(number, offset); + isSuccess = true; + pointer += 3; // Pointer goes to next keyword +} + +void QueryHelper::HandleBeginGroup(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + dbQuery.BeginGroup(); + isSuccess = true; + pointer += 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleEndGroup(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + dbQuery.EndGroup(); + isSuccess = true; + pointer += 1; // Pointer goes to next keyword +} + +void QueryHelper::HandleKeyPrefix(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("KeyPrefix not enough params."); + isSuccess = false; + return; + } + const std::string &prefix = deviceId_ + StringToString(words.at(pointer + 1)); // prefix + const std::vector prefixVector(prefix.begin(), prefix.end()); + dbQuery.PrefixKey(prefixVector); + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +void QueryHelper::HandleSetSuggestIndex(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + QUERY_SKIP_SIZE > end) { + ZLOGE("HandleSetSuggestIndex not enough params."); + isSuccess = false; + return; + } + std::string index = StringToString(words.at(pointer + QUERY_SKIP_SIZE)); + dbQuery.SuggestIndex(index); + isSuccess = true; + pointer += QUERY_WORD_SIZE; +} + +void QueryHelper::HandleDeviceId(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery) { + if (pointer + 1 > end) { // This keyword has 1 following params + ZLOGE("DeviceId not enough params."); + isSuccess = false; + return; + } + deviceId_ = StringToString(words.at(pointer + 1)); // deviceId + ZLOGI("query devId string length:%zu", deviceId_.length()); + deviceId_ = KvStoreUtils::GetProviderInstance().GetUuidByNodeId(deviceId_); // convert to UUId + ZLOGI("query converted devId string length:%zu", deviceId_.length()); + if (!hasPrefixKey_) { + ZLOGD("DeviceId as the only prefixKey."); + const std::vector prefixVector(deviceId_.begin(), deviceId_.end()); + dbQuery.PrefixKey(prefixVector); + } else { + ZLOGD("Join deviceId with user specified prefixkey later."); + } + isSuccess = true; + pointer += 2; // Pointer goes to next keyword +} + +int QueryHelper::StringToInt(const std::string &word) { + int result; + std::istringstream(word) >> result; + return result; +} + +int64_t QueryHelper::StringToLong(const std::string &word) { + int64_t result; + std::istringstream(word) >> result; + return result; +} + +double QueryHelper::StringToDouble(const std::string &word) { + double result; + std::istringstream(word) >> result; + return result; +} + +bool QueryHelper::StringToBoolean(const std::string &word) { + if (word == DataQuery::VALUE_TRUE) { + return true; + } else if (word == DataQuery::VALUE_FALSE) { + return false; + } else { + ZLOGE("StringToBoolean wrong value."); + return false; + } +} + +std::string QueryHelper::StringToString(const std::string &word) { + std::string result = word; + if (result.compare(DataQuery::EMPTY_STRING) == 0) { + result = ""; + return result; + } + size_t index = 0; // search from the beginning of the string + while (true) { + index = result.find(DataQuery::SPACE_ESCAPE, index); + if (index == std::string::npos) { + break; + } + result.replace(index, 2, DataQuery::SPACE); // 2 chars to be replaced + index += 1; // replaced with 1 char, keep searching the remaining string + } + index = 0; // search from the beginning of the string + while (true) { + index = result.find(DataQuery::SPECIAL_ESCAPE, index); + if (index == std::string::npos) { + break; + } + result.replace(index, 3, DataQuery::SPECIAL); // 3 chars to be replaced + index += 1; // replaced with 1 char, keep searching the remaining string + } + return result; +} + +std::vector QueryHelper::GetIntegerList(const std::vector &words, + int &elementPointer, const int &end) { + std::vector valueList; + bool isEndFound = false; + while (elementPointer <= end) { + if (words.at(elementPointer) == DataQuery::END_IN) { + isEndFound = true; + break; + } + valueList.push_back(StringToInt(words.at(elementPointer))); + elementPointer++; + } + if (isEndFound) { + return valueList; + } else { + ZLOGE("GetIntegerList failed."); + return std::vector(); + } +} + +std::vector QueryHelper::GetLongList(const std::vector &words, + int &elementPointer, const int &end) { + std::vector valueList; + bool isEndFound = false; + while (elementPointer <= end) { + if (words.at(elementPointer) == DataQuery::END_IN) { + isEndFound = true; + break; + } + valueList.push_back(StringToLong(words.at(elementPointer))); + elementPointer++; + } + if (isEndFound) { + return valueList; + } else { + ZLOGE("GetLongList failed."); + return std::vector(); + } +} + +std::vector QueryHelper::GetDoubleList(const std::vector &words, + int &elementPointer, const int &end) { + std::vector valueList; + bool isEndFound = false; + while (elementPointer <= end) { + if (words.at(elementPointer) == DataQuery::END_IN) { + isEndFound = true; + break; + } + valueList.push_back(StringToDouble(words.at(elementPointer))); + elementPointer++; + } + if (isEndFound) { + return valueList; + } else { + ZLOGE("GetDoubleList failed."); + return std::vector(); + } +} + +std::vector QueryHelper::GetStringList(const std::vector &words, + int &elementPointer, const int &end) { + std::vector valueList; + bool isEndFound = false; + while (elementPointer <= end) { + if (words.at(elementPointer) == DataQuery::END_IN) { + isEndFound = true; + break; + } + valueList.push_back(StringToString(words.at(elementPointer))); + elementPointer++; + } + if (isEndFound) { + return valueList; + } else { + ZLOGE("GetStringList failed."); + return std::vector(); + } +} +} // namespace OHOS::DistributedKv diff --git a/services/distributeddataservice/app/src/query_helper.h b/services/distributeddataservice/app/src/query_helper.h new file mode 100755 index 000000000..158b25b55 --- /dev/null +++ b/services/distributeddataservice/app/src/query_helper.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 QUERY_HELPER_H +#define QUERY_HELPER_H + +#include "query.h" + +namespace OHOS::DistributedKv { +class QueryHelper { +public: + static DistributedDB::Query StringToDbQuery(const std::string &query, bool &isSuccess); +private: + static std::string deviceId_; + static bool hasPrefixKey_; + static void Handle(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleExtra(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleNotEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleGreaterThan(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleLessThan(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleGreaterThanOrEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleLessThanOrEqualTo(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleIsNull(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleIsNotNull(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleIn(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleNotIn(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleLike(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleNotLike(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleAnd(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleOr(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleOrderByAsc(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleOrderByDesc(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleLimit(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleBeginGroup(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleEndGroup(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleKeyPrefix(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleSetSuggestIndex(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static void HandleDeviceId(const std::vector &words, int &pointer, + const int &end, bool &isSuccess, DistributedDB::Query &dbQuery); + static int StringToInt(const std::string &word); + static int64_t StringToLong(const std::string &word); + static double StringToDouble(const std::string &word); + static bool StringToBoolean(const std::string &word); + static std::string StringToString(const std::string &word); + static std::vector GetIntegerList(const std::vector &words, int &elementPointer, const int &end); + static std::vector GetLongList(const std::vector &words, int &elementPointer, const int &end); + static std::vector GetDoubleList(const std::vector &words, + int &elementPointer, const int &end); + static std::vector GetStringList(const std::vector &words, + int &elementPointer, const int &end); +}; +} // namespace OHOS::DistributedKv +#endif // QUERY_HELPER_H diff --git a/services/distributeddataservice/app/src/single_kvstore_impl.cpp b/services/distributeddataservice/app/src/single_kvstore_impl.cpp new file mode 100755 index 000000000..affe51662 --- /dev/null +++ b/services/distributeddataservice/app/src/single_kvstore_impl.cpp @@ -0,0 +1,1500 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SingleKvStoreImpl" + +#include "single_kvstore_impl.h" +#include +#include "account_delegate.h" +#include "backup_handler.h" +#include "constant.h" +#include "dds_trace.h" +#include "device_kvstore_resultset_impl.h" +#include "device_kvstore_impl.h" +#include "device_kvstore_observer_impl.h" +#include "kvstore_app_manager.h" +#include "kvstore_data_service.h" +#include "kvstore_meta_manager.h" +#include "kvstore_sync_manager.h" +#include "kvstore_utils.h" +#include "ipc_skeleton.h" +#include "log_print.h" +#include "permission_validator.h" +#include "query_helper.h" +#include "reporter.h" + +namespace OHOS::DistributedKv { +static bool TaskIsBackground(pid_t pid) +{ + std::ifstream ifs("/proc/" + std::to_string(pid) + "/cgroup", std::ios::in); + ZLOGD("pid %d open %d", pid, ifs.good()); + if (!ifs.good()) { + return false; + } + + while (!ifs.eof()) { + const int cgroupLineLen = 256; // enough + char buffer[cgroupLineLen] = { 0 }; + ifs.getline(buffer, sizeof(buffer)); + std::string line = buffer; + + size_t pos = line.find("background"); + if (pos != std::string::npos) { + ifs.close(); + return true; + } + } + ifs.close(); + return false; +} + +SingleKvStoreImpl::~SingleKvStoreImpl() +{ + RemoveAllSyncOperation(); + ZLOGI("destructor"); +} + +SingleKvStoreImpl::SingleKvStoreImpl(const Options &options, const std::string &deviceAccountId, + const std::string &bundleName, const std::string &storeId, + const std::string &appDirectory, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) + : options_(options), + deviceAccountId_(deviceAccountId), + bundleName_(bundleName), + storeId_(storeId), + storePath_(Constant::Concatenate({ appDirectory, storeId })), + kvStoreNbDelegate_(kvStoreNbDelegate), + observerMapMutex_(), + observerMap_(), + storeResultSetMutex_(), + storeResultSetMap_(), + openCount_(1), + flowCtrlManager_(BURST_CAPACITY, SUSTAINED_CAPACITY) +{ +} + +Status SingleKvStoreImpl::Put(const Key &key, const Value &value) +{ + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + auto trimmedKey = Constant::TrimCopy>(key.Data()); + // Restrict key and value size to interface specification. + if (trimmedKey.size() == 0 || trimmedKey.size() > Constant::MAX_KEY_LENGTH || + value.Size() > Constant::MAX_VALUE_LENGTH) { + ZLOGW("invalid_argument."); + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::Value tmpValue = value.Data(); + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Put(tmpKey, tmpValue); + } + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("succeed."); + return Status::SUCCESS; + } + ZLOGW("failed status: %d.", static_cast(status)); + + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Put failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + + return ConvertDbStatus(status); +} + +Status SingleKvStoreImpl::ConvertDbStatus(DistributedDB::DBStatus status) +{ + switch (status) { + case DistributedDB::DBStatus::OK: + return Status::SUCCESS; + case DistributedDB::DBStatus::INVALID_ARGS: + return Status::INVALID_ARGUMENT; + case DistributedDB::DBStatus::NOT_FOUND: + return Status::KEY_NOT_FOUND; + case DistributedDB::DBStatus::INVALID_VALUE_FIELDS: + return Status::INVALID_VALUE_FIELDS; + case DistributedDB::DBStatus::INVALID_FIELD_TYPE: + return Status::INVALID_FIELD_TYPE; + case DistributedDB::DBStatus::CONSTRAIN_VIOLATION: + return Status::CONSTRAIN_VIOLATION; + case DistributedDB::DBStatus::INVALID_FORMAT: + return Status::INVALID_FORMAT; + case DistributedDB::DBStatus::INVALID_QUERY_FORMAT: + return Status::INVALID_QUERY_FORMAT; + case DistributedDB::DBStatus::INVALID_QUERY_FIELD: + return Status::INVALID_QUERY_FIELD; + case DistributedDB::DBStatus::NOT_SUPPORT: + return Status::NOT_SUPPORT; + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: + break; + } + return Status::ERROR; +} + +Status SingleKvStoreImpl::Delete(const Key &key) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + auto trimmedKey = DistributedKv::Constant::TrimCopy>(key.Data()); + if (trimmedKey.size() == 0 || trimmedKey.size() > Constant::MAX_KEY_LENGTH) { + ZLOGW("invalid argument."); + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Delete(tmpKey); + } + if (status == DistributedDB::DBStatus::OK) { + ZLOGD("succeed."); + return Status::SUCCESS; + } + ZLOGW("failed status: %d.", static_cast(status)); + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Delete failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + return ConvertDbStatus(status); +} + +Status SingleKvStoreImpl::Get(const Key &key, Value &value) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + auto trimmedKey = DistributedKv::Constant::TrimCopy>(key.Data()); + if (trimmedKey.empty() || trimmedKey.size() > DistributedKv::Constant::MAX_KEY_LENGTH) { + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::Key tmpKey = trimmedKey; + DistributedDB::Value tmpValue; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Get(tmpKey, tmpValue); + } + ZLOGD("status: %d.", static_cast(status)); + if (status == DistributedDB::DBStatus::OK) { + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + // Value don't have other write method. + Value tmpValueForCopy(tmpValue); + value = tmpValueForCopy; + return Status::SUCCESS; + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGI("Get failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + return ConvertDbStatus(status); +} + +Status SingleKvStoreImpl::SubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + return SubscribeKvStore(subscribeType, observer, false); +} + +Status SingleKvStoreImpl::SubscribeKvStore(const SubscribeType subscribeType, sptr observer, + bool deviceCoordinate) +{ + ZLOGD("start."); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + if (observer == nullptr) { + return Status::INVALID_ARGUMENT; + } + std::shared_lock sharedLock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + KvStoreObserverImpl *nbObserver = new (std::nothrow) DeviceKvStoreObserverImpl(subscribeType, observer, + deviceCoordinate); + if (nbObserver == nullptr) { + ZLOGW("new KvStoreObserverNbImpl failed"); + return Status::ERROR; + } + + std::lock_guard lock(observerMapMutex_); + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + bool alreadySubscribed = (observerMap_.find(objectPtr) != observerMap_.end()); + if (alreadySubscribed) { + delete nbObserver; + return Status::STORE_ALREADY_SUBSCRIBE; + } + int dbObserverMode = ConvertToDbObserverMode(subscribeType); + DistributedDB::Key emptyKey; + DistributedDB::DBStatus dbStatus; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = kvStoreNbDelegate_->RegisterObserver(emptyKey, dbObserverMode, nbObserver); + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + if (dbStatus == DistributedDB::DBStatus::OK) { + observerMap_.insert(std::pair(objectPtr, nbObserver)); + return Status::SUCCESS; + } + + delete nbObserver; + if (dbStatus == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + if (dbStatus == DistributedDB::DBStatus::DB_ERROR) { + return Status::DB_ERROR; + } + return Status::ERROR; +} + +// Convert KvStore subscribe type to DistributeDB observer mode. +int SingleKvStoreImpl::ConvertToDbObserverMode(const SubscribeType subscribeType) const +{ + int dbObserverMode; + if (subscribeType == SubscribeType::SUBSCRIBE_TYPE_LOCAL) { + dbObserverMode = DistributedDB::OBSERVER_CHANGES_NATIVE; + } else if (subscribeType == SubscribeType::SUBSCRIBE_TYPE_REMOTE) { + dbObserverMode = DistributedDB::OBSERVER_CHANGES_FOREIGN; + } else { + dbObserverMode = DistributedDB::OBSERVER_CHANGES_FOREIGN | DistributedDB::OBSERVER_CHANGES_NATIVE; + } + return dbObserverMode; +} + +Status SingleKvStoreImpl::UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + if (observer == nullptr) { + ZLOGW("observer invalid."); + return Status::INVALID_ARGUMENT; + } + std::shared_lock sharedLock(storeNbDelegateMutex_); + std::lock_guard lock(observerMapMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + auto nbObserver = observerMap_.find(objectPtr); + if (nbObserver == observerMap_.end()) { + ZLOGW("No existing observer to unsubscribe. Return success."); + return Status::SUCCESS; + } + DistributedDB::DBStatus dbStatus; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = kvStoreNbDelegate_->UnRegisterObserver(nbObserver->second); + } + if (dbStatus == DistributedDB::DBStatus::OK) { + delete nbObserver->second; + observerMap_.erase(objectPtr); + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + if (dbStatus == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + + ZLOGW("failed code=%d.", static_cast(dbStatus)); + if (dbStatus == DistributedDB::DBStatus::NOT_FOUND) { + return Status::STORE_NOT_SUBSCRIBE; + } + if (dbStatus == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + return Status::ERROR; +} + +Status SingleKvStoreImpl::GetEntries(const Key &prefixKey, std::vector &entries) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + auto trimmedPrefix = Constant::TrimCopy>(prefixKey.Data()); + if (trimmedPrefix.size() > Constant::MAX_KEY_LENGTH) { + return Status::INVALID_ARGUMENT; + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::Key tmpKeyPrefix = trimmedPrefix; + std::vector dbEntries; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->GetEntries(tmpKeyPrefix, dbEntries); + } + ZLOGI("result DBStatus: %d", static_cast(status)); + if (status == DistributedDB::DBStatus::OK) { + entries.reserve(dbEntries.size()); + ZLOGD("vector size: %zu status: %d.", dbEntries.size(), static_cast(status)); + for (auto const &dbEntry : dbEntries) { + Key tmpKey(dbEntry.key); + Value tmpValue(dbEntry.value); + Entry entry; + entry.key = tmpKey; + entry.value = tmpValue; + entries.push_back(entry); + } + return Status::SUCCESS; + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("GetEntries failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + if (status == DistributedDB::DBStatus::BUSY || status == DistributedDB::DBStatus::DB_ERROR) { + return Status::DB_ERROR; + } + if (status == DistributedDB::DBStatus::NOT_FOUND) { + ZLOGI("DB return NOT_FOUND, no matching result. Return success with empty list."); + return Status::SUCCESS; + } + if (status == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + if (status == DistributedDB::DBStatus::EKEYREVOKED_ERROR || + status == DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR) { + return Status::SECURITY_LEVEL_ERROR; + } + return Status::ERROR; +} + +Status SingleKvStoreImpl::GetEntriesWithQuery(const std::string &query, std::vector &entries) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + ZLOGI("begin"); + bool isSuccess = false; + DistributedDB::Query dbQuery = QueryHelper::StringToDbQuery(query, isSuccess); + if (!isSuccess) { + ZLOGE("StringToDbQuery failed."); + return Status::INVALID_ARGUMENT; + } else { + ZLOGD("StringToDbQuery success."); + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + std::vector dbEntries; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->GetEntries(dbQuery, dbEntries); + } + ZLOGI("result DBStatus: %d", static_cast(status)); + if (status == DistributedDB::DBStatus::OK) { + entries.reserve(dbEntries.size()); + ZLOGD("vector size: %zu status: %d.", dbEntries.size(), static_cast(status)); + for (auto const &dbEntry : dbEntries) { + Key tmpKey(dbEntry.key); + Value tmpValue(dbEntry.value); + Entry entry; + entry.key = tmpKey; + entry.value = tmpValue; + entries.push_back(entry); + } + return Status::SUCCESS; + } + switch (status) { + case DistributedDB::DBStatus::BUSY: + case DistributedDB::DBStatus::DB_ERROR: { + return Status::DB_ERROR; + } + case DistributedDB::DBStatus::INVALID_ARGS: { + return Status::INVALID_ARGUMENT; + } + case DistributedDB::DBStatus::INVALID_QUERY_FORMAT: { + return Status::INVALID_QUERY_FORMAT; + } + case DistributedDB::DBStatus::INVALID_QUERY_FIELD: { + return Status::INVALID_QUERY_FIELD; + } + case DistributedDB::DBStatus::NOT_SUPPORT: { + return Status::NOT_SUPPORT; + } + case DistributedDB::DBStatus::NOT_FOUND: { + ZLOGI("DB return NOT_FOUND, no matching result. Return success with empty list."); + return Status::SUCCESS; + } + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: { + return Status::ERROR; + } + } +} + +void SingleKvStoreImpl::GetResultSet(const Key &prefixKey, + std::function)> callback, + bool deviceCoordinate) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + callback(Status::EXCEED_MAX_ACCESS_RATE, nullptr); + return; + } + auto trimmedPrefix = Constant::TrimCopy>(prefixKey.Data()); + if (trimmedPrefix.size() > Constant::MAX_KEY_LENGTH) { + callback(Status::INVALID_ARGUMENT, nullptr); + return; + } + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + callback(Status::ILLEGAL_STATE, nullptr); + return; + } + DistributedDB::Key tmpKeyPrefix = trimmedPrefix; + DistributedDB::KvStoreResultSet *dbResultSet = nullptr; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->GetEntries(tmpKeyPrefix, dbResultSet); + } + ZLOGI("result DBStatus: %d", static_cast(status)); + if (status == DistributedDB::DBStatus::OK) { + std::lock_guard lg(storeResultSetMutex_); + KvStoreResultSetImpl *storeResultSetImpl = new DeviceKvStoreResultSetImpl(tmpKeyPrefix, dbResultSet, + deviceCoordinate); + sptr storeResultSet = storeResultSetImpl; + callback(Status::SUCCESS, storeResultSet); + storeResultSetMap_.emplace(storeResultSetImpl, storeResultSet); + return; + } + switch (status) { + case DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB: { + ZLOGE("GetResultSet failed, distributeddb need recover."); + bool success = Import(bundleName_); + callback(success ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED, nullptr); + break; + } + case DistributedDB::DBStatus::BUSY: // fallthrough + case DistributedDB::DBStatus::DB_ERROR: + callback(Status::DB_ERROR, nullptr); + break; + case DistributedDB::DBStatus::NOT_FOUND: + callback(Status::KEY_NOT_FOUND, nullptr); + break; + case DistributedDB::DBStatus::INVALID_ARGS: + callback(Status::INVALID_ARGUMENT, nullptr); + break; + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + callback(Status::SECURITY_LEVEL_ERROR, nullptr); + break; + default: + callback(Status::ERROR, nullptr); + break; + } +} + +void SingleKvStoreImpl::GetResultSet(const Key &prefixKey, + std::function)> callback) +{ + GetResultSet(prefixKey, callback, false); +} + +void SingleKvStoreImpl::GetResultSetWithQuery(const std::string &query, + std::function)> callback) +{ + GetResultSetWithQuery(query, callback, false); +} + +void SingleKvStoreImpl::GetResultSetWithQuery(const std::string &query, + std::function)> callback, + bool deviceCoordinate) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + callback(Status::EXCEED_MAX_ACCESS_RATE, nullptr); + return; + } + bool isSuccess = false; + DistributedDB::Query dbQuery = QueryHelper::StringToDbQuery(query, isSuccess); + if (!isSuccess) { + ZLOGE("StringToDbQuery failed."); + return; + } else { + ZLOGD("StringToDbQuery success."); + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + callback(Status::ILLEGAL_STATE, nullptr); + return; + } + DistributedDB::KvStoreResultSet *dbResultSet = nullptr; + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->GetEntries(dbQuery, dbResultSet); + } + ZLOGI("result DBStatus: %d", static_cast(status)); + if (status == DistributedDB::DBStatus::OK) { + std::lock_guard lg(storeResultSetMutex_); + KvStoreResultSetImpl *storeResultSetImpl = new DeviceKvStoreResultSetImpl(dbResultSet, deviceCoordinate); + sptr storeResultSet = storeResultSetImpl; + callback(Status::SUCCESS, storeResultSet); + storeResultSetMap_.emplace(storeResultSetImpl, storeResultSet); + return; + } + switch (status) { + case DistributedDB::DBStatus::BUSY: + case DistributedDB::DBStatus::DB_ERROR: { + callback(Status::DB_ERROR, nullptr); + break; + } + case DistributedDB::DBStatus::INVALID_ARGS: { + callback(Status::INVALID_ARGUMENT, nullptr); + break; + } + case DistributedDB::DBStatus::INVALID_QUERY_FORMAT: { + callback(Status::INVALID_QUERY_FORMAT, nullptr); + break; + } + case DistributedDB::DBStatus::INVALID_QUERY_FIELD: { + callback(Status::INVALID_QUERY_FIELD, nullptr); + break; + } + case DistributedDB::DBStatus::NOT_SUPPORT: { + callback(Status::NOT_SUPPORT, nullptr); + break; + } + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + callback(Status::SECURITY_LEVEL_ERROR, nullptr); + break; + default: { + callback(Status::ERROR, nullptr); + break; + } + } +} + +Status SingleKvStoreImpl::GetCountWithQuery(const std::string &query, int &result) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + ZLOGI("begin"); + bool isSuccess = false; + DistributedDB::Query dbQuery = QueryHelper::StringToDbQuery(query, isSuccess); + if (!isSuccess) { + ZLOGE("StringToDbQuery failed."); + return Status::INVALID_ARGUMENT; + } else { + ZLOGD("StringToDbQuery success."); + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->GetCount(dbQuery, result); + } + ZLOGI("result DBStatus: %d", static_cast(status)); + switch (status) { + case DistributedDB::DBStatus::OK: { + return Status::SUCCESS; + } + case DistributedDB::DBStatus::BUSY: + case DistributedDB::DBStatus::DB_ERROR: { + return Status::DB_ERROR; + } + case DistributedDB::DBStatus::INVALID_ARGS: { + return Status::INVALID_ARGUMENT; + } + case DistributedDB::DBStatus::INVALID_QUERY_FORMAT: { + return Status::INVALID_QUERY_FORMAT; + } + case DistributedDB::DBStatus::INVALID_QUERY_FIELD: { + return Status::INVALID_QUERY_FIELD; + } + case DistributedDB::DBStatus::NOT_SUPPORT: { + return Status::NOT_SUPPORT; + } + case DistributedDB::DBStatus::NOT_FOUND: { + ZLOGE("DB return NOT_FOUND, no matching result. Return success with count 0."); + result = 0; + return Status::SUCCESS; + } + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: { + return Status::ERROR; + } + } +} + +Status SingleKvStoreImpl::CloseResultSet(sptr resultSet) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (resultSet == nullptr) { + return Status::INVALID_ARGUMENT; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + std::shared_lock lock(storeNbDelegateMutex_); + std::lock_guard lg(storeResultSetMutex_); + KvStoreResultSetImpl *kvStoreResultSetImpl = static_cast(resultSet.GetRefPtr()); + Status status; + auto it = storeResultSetMap_.find(kvStoreResultSetImpl); + if (it == storeResultSetMap_.end()) { + ZLOGE("ResultSet not found in this store."); + return Status::INVALID_ARGUMENT; + } + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreResultSetImpl->CloseResultSet(kvStoreNbDelegate_); + } + if (status == Status::SUCCESS) { + storeResultSetMap_.erase(it); + } else { + ZLOGE("CloseResultSet failed."); + } + return status; +} + +Status SingleKvStoreImpl::RemoveDeviceData(const std::string &device) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + // map UUID to UDID + std::string deviceUDID = KvStoreUtils::GetProviderInstance().GetUuidByNodeId(device); + if (deviceUDID.empty()) { + ZLOGE("can't get nodeid"); + return Status::ERROR; + } + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->RemoveDeviceData(deviceUDID); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("RemoveDeviceData failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + if (status == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + if (status == DistributedDB::DBStatus::EKEYREVOKED_ERROR || + status == DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR) { + return Status::SECURITY_LEVEL_ERROR; + } + return Status::ERROR; +} + +Status SingleKvStoreImpl::Sync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t allowedDelayMs) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + ZLOGD("start."); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + uint32_t delayMs = GetSyncDelayTime(allowedDelayMs); + { + std::unique_lock lock(storeNbDelegateMutex_); + if ((waitingSyncCount_ > 0) && + (lastSyncDeviceIdList_ == deviceIdList) && (lastSyncMode_ == mode) && (lastSyncDelayMs_ == delayMs)) { + return Status::SUCCESS; + } + lastSyncDeviceIdList_ = deviceIdList; + lastSyncMode_ = mode; + lastSyncDelayMs_ = delayMs; + } + return AddSync(deviceIdList, mode, delayMs); +} + +Status SingleKvStoreImpl::AddSync(const std::vector &deviceIdList, const SyncMode &mode, + uint32_t delayMs) +{ + ZLOGD("start."); + waitingSyncCount_++; + return KvStoreSyncManager::GetInstance()->AddSyncOperation(reinterpret_cast(this), delayMs, + std::bind(&SingleKvStoreImpl::DoSync, this, deviceIdList, mode, std::placeholders::_1), + std::bind(&SingleKvStoreImpl::DoSyncComplete, this, std::placeholders::_1)); +} + +uint32_t SingleKvStoreImpl::GetSyncDelayTime(uint32_t allowedDelayMs) const +{ + uint32_t delayMs = allowedDelayMs; + if (delayMs == 0) { + bool isBackground = TaskIsBackground(IPCSkeleton::GetCallingPid()); + if (isBackground) { + // delay schedule + delayMs = defaultSyncDelayMs_ ? defaultSyncDelayMs_ : KvStoreSyncManager::SYNC_DEFAULT_DELAY_MS; + } + } else { + if (delayMs < KvStoreSyncManager::SYNC_MIN_DELAY_MS) { + delayMs = KvStoreSyncManager::SYNC_MIN_DELAY_MS; + } + if (delayMs > KvStoreSyncManager::SYNC_MAX_DELAY_MS) { + delayMs = KvStoreSyncManager::SYNC_MAX_DELAY_MS; + } + } + return delayMs; +} + +Status SingleKvStoreImpl::RemoveAllSyncOperation() +{ + return KvStoreSyncManager::GetInstance()->RemoveSyncOperation(reinterpret_cast(this)); +} + +void SingleKvStoreImpl::DoSyncComplete(const std::map &devicesSyncResult) +{ + DdsTrace trace(std::string("DdsTrace " LOG_TAG "::") + std::string(__FUNCTION__)); + std::map resultMap; + for (auto device : devicesSyncResult) { + if (device.second == DistributedDB::DBStatus::OK) { + resultMap[device.first] = Status::SUCCESS; + } else if (device.second == DistributedDB::DBStatus::NOT_FOUND) { + resultMap[device.first] = Status::DEVICE_NOT_FOUND; + } else if (device.second == DistributedDB::DBStatus::TIME_OUT) { + resultMap[device.first] = Status::TIME_OUT; + } else { + resultMap[device.first] = Status::ERROR; + } + } + syncRetries_ = 0; + ZLOGD("callback."); + if (syncCallback_ != nullptr) { + syncCallback_->SyncCompleted(resultMap); + } +} + +Status SingleKvStoreImpl::DoSync(const std::vector &deviceIdList, const SyncMode &mode, + const KvStoreSyncManager::SyncEnd &syncEnd) +{ + ZLOGD("start."); + std::vector deviceUuidList; + for (auto const &device : deviceIdList) { + std::string nodeid = KvStoreUtils::GetProviderInstance().GetUuidByNodeId(device); + if (!nodeid.empty()) { + deviceUuidList.push_back(nodeid); + } else { + ZLOGW("invalid deviceId:%s.", KvStoreUtils::ToBeAnonymous(device).c_str()); + } + } + if (deviceUuidList.empty()) { + ZLOGE("not found deviceIds."); + return Status::ERROR; + } + DistributedDB::SyncMode dbMode; + if (mode == SyncMode::PUSH) { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PUSH_ONLY; + } else if (mode == SyncMode::PULL) { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PULL_ONLY; + } else { + dbMode = DistributedDB::SyncMode::SYNC_MODE_PUSH_PULL; + } + + DistributedDB::DBStatus status; + { + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("kvstore is not open"); + return Status::ILLEGAL_STATE; + } + waitingSyncCount_--; + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Sync(deviceUuidList, dbMode, syncEnd); + ZLOGD("end: %d", static_cast(status)); + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + if (status == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + if (status == DistributedDB::DBStatus::BUSY) { + if (syncRetries_ < KvStoreSyncManager::SYNC_RETRY_MAX_COUNT) { + syncRetries_++; + auto addStatus = AddSync(deviceUuidList, mode, KvStoreSyncManager::SYNC_DEFAULT_DELAY_MS); + if (addStatus == Status::SUCCESS) { + return addStatus; + } + } + } + if (status == DistributedDB::DBStatus::DB_ERROR) { + return Status::DB_ERROR; + } + if (status == DistributedDB::DBStatus::NOT_FOUND) { + return Status::DEVICE_NOT_FOUND; + } + if (status == DistributedDB::DBStatus::INVALID_ARGS) { + return Status::INVALID_ARGUMENT; + } + return Status::ERROR; +} + +InnerStatus SingleKvStoreImpl::Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGW("start Close"); + if (openCount_ > 1) { + openCount_--; + return InnerStatus::DECREASE_REFCOUNT; + } + Status status = ForceClose(kvStoreDelegateManager); + if (status == Status::SUCCESS) { + return InnerStatus::SUCCESS; + } + return InnerStatus::ERROR; +} + +Status SingleKvStoreImpl::ForceClose(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("start, current openCount is %d.", openCount_); + std::unique_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr || kvStoreDelegateManager == nullptr) { + ZLOGW("get nullptr"); + return Status::INVALID_ARGUMENT; + } + RemoveAllSyncOperation(); + ZLOGI("start to clean observer"); + std::lock_guard observerMapLockGuard(observerMapMutex_); + for (auto observer = observerMap_.begin(); observer != observerMap_.end();) { + DistributedDB::DBStatus dbStatus = kvStoreNbDelegate_->UnRegisterObserver(observer->second); + if (dbStatus == DistributedDB::DBStatus::OK) { + delete observer->second; + observer = observerMap_.erase(observer); + } else { + ZLOGW("UnSubscribeKvStore failed during ForceClose, status %d.", dbStatus); + return Status::ERROR; + } + } + ZLOGI("start to clean resultset"); + std::lock_guard lg(storeResultSetMutex_); + for (auto resultSetPair = storeResultSetMap_.begin(); resultSetPair != storeResultSetMap_.end();) { + Status status = (resultSetPair->first)->CloseResultSet(kvStoreNbDelegate_); + if (status != Status::SUCCESS) { + ZLOGW("CloseResultSet failed during ForceClose, errCode %d", status); + return status; + } + resultSetPair = storeResultSetMap_.erase(resultSetPair); + } + DistributedDB::DBStatus status = kvStoreDelegateManager->CloseKvStore(kvStoreNbDelegate_); + if (status == DistributedDB::DBStatus::OK) { + kvStoreNbDelegate_ = nullptr; + ZLOGI("end."); + return Status::SUCCESS; + } + ZLOGI("failed with error code %d.", status); + return Status::ERROR; +} + +Status SingleKvStoreImpl::MigrateKvStore(const std::string &harmonyAccountId, + const std::string &kvStoreDataDir, + DistributedDB::KvStoreDelegateManager *oldDelegateMgr, + DistributedDB::KvStoreDelegateManager *&newDelegateMgr) +{ + ZLOGI("begin."); + std::unique_lock lock(storeNbDelegateMutex_); + if (oldDelegateMgr == nullptr) { + ZLOGW("kvStore delegate manager is nullptr."); + return Status::INVALID_ARGUMENT; + } + + ZLOGI("create new KvStore."); + std::vector secretKey; // expected get secret key from meta kvstore successful when encrypt flag is true. + std::unique_ptr, void(*)(std::vector*)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + bool outdated = false; // ignore outdated flag during rebuild kvstore. + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId_, "default", bundleName_, storeId_, + "SINGLE_KEY"); + if (options_.encrypt) { + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGE("Get secret key from meta kvstore failed."); + return Status::CRYPT_ERROR; + } + } + + DistributedDB::DBStatus dbStatus; + DistributedDB::KvStoreNbDelegate::Option dbOption; + Status status = KvStoreAppManager::InitNbDbOption(options_, secretKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitNbDbOption failed."); + return status; + } + + if (newDelegateMgr == nullptr) { + auto appId = KvStoreUtils::GetAppIdByBundleName(bundleName_); + if (appId.empty()) { + ZLOGE("Get appId by bundle name failed."); + return Status::MIGRATION_KVSTORE_FAILED; + } + newDelegateMgr = new (std::nothrow) DistributedDB::KvStoreDelegateManager(appId, harmonyAccountId); + if (newDelegateMgr == nullptr) { + ZLOGE("new KvStoreDelegateManager failed."); + return Status::MIGRATION_KVSTORE_FAILED; + } + DistributedDB::KvStoreConfig kvStoreConfig; + kvStoreConfig.dataDir = kvStoreDataDir; + newDelegateMgr->SetKvStoreConfig(kvStoreConfig); + } + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate = nullptr; // new KvStoreNbDelegate get from distributed DB. + newDelegateMgr->GetKvStore( + storeId_, dbOption, + [&](DistributedDB::DBStatus status, DistributedDB::KvStoreNbDelegate *delegate) { + kvStoreNbDelegate = delegate; + dbStatus = status; + }); + if (kvStoreNbDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr, dbStatusTmp: %d", static_cast(dbStatus)); + return Status::DB_ERROR; + } + + if (options_.autoSync) { + bool autoSync = true; + auto data = static_cast(&autoSync); + auto pragmaStatus = kvStoreNbDelegate->Pragma(DistributedDB::PragmaCmd::AUTO_SYNC, data); + if (pragmaStatus != DistributedDB::DBStatus::OK) { + ZLOGE("pragmaStatus: %d", static_cast(pragmaStatus)); + } + } + + status = RebuildKvStoreObserver(kvStoreNbDelegate); + if (status != Status::SUCCESS) { + ZLOGI("rebuild KvStore observer failed, errCode %d.", static_cast(status)); + // skip this failed, continue to do other rebuild process. + } + + status = RebuildKvStoreResultSet(); + if (status != Status::SUCCESS) { + ZLOGI("rebuild KvStore resultset failed, errCode %d.", static_cast(status)); + // skip this failed, continue to do close kvstore process. + } + + ZLOGI("close old KvStore."); + dbStatus = oldDelegateMgr->CloseKvStore(kvStoreNbDelegate_); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGI("rebuild KvStore failed during close KvStore, errCode %d.", static_cast(status)); + newDelegateMgr->CloseKvStore(kvStoreNbDelegate); + return Status::DB_ERROR; + } + + ZLOGI("update kvstore delegate."); + kvStoreNbDelegate_ = kvStoreNbDelegate; + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::RebuildKvStoreObserver(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) +{ + ZLOGI("rebuild observer."); + if (kvStoreNbDelegate_ == nullptr || kvStoreNbDelegate == nullptr) { + ZLOGI("RebuildKvStoreObserver illlegal."); + return Status::ILLEGAL_STATE; + } + std::lock_guard observerMapLockGuard(observerMapMutex_); + Status status = Status::SUCCESS; + DistributedDB::DBStatus dbStatus; + DistributedDB::Key emptyKey; + for (const auto &observerPair : observerMap_) { + dbStatus = kvStoreNbDelegate_->UnRegisterObserver(observerPair.second); + if (dbStatus != DistributedDB::OK) { + status = Status::DB_ERROR; + ZLOGW("rebuild observer failed during UnRegisterObserver, status %d.", static_cast(dbStatus)); + continue; + } + dbStatus = kvStoreNbDelegate->RegisterObserver(emptyKey, + static_cast(ConvertToDbObserverMode(observerPair.second->GetSubscribeType())), + observerPair.second); + if (dbStatus != DistributedDB::OK) { + status = Status::DB_ERROR; + ZLOGW("rebuild observer failed during RegisterObserver, status %d.", static_cast(dbStatus)); + continue; + } + } + return status; +} + +Status SingleKvStoreImpl::RebuildKvStoreResultSet() +{ + if (kvStoreNbDelegate_ == nullptr) { + return Status::INVALID_ARGUMENT; + } + ZLOGI("rebuild resultset"); + std::lock_guard lg(storeResultSetMutex_); + Status retStatus = Status::SUCCESS; + for (const auto &resultSetPair : storeResultSetMap_) { + Status status = (resultSetPair.first)->MigrateKvStore(kvStoreNbDelegate_); + if (status != Status::SUCCESS) { + retStatus = status; + ZLOGW("rebuild resultset failed, errCode %d", static_cast(status)); + continue; + } + } + return retStatus; +} + +Status SingleKvStoreImpl::ReKey(const std::vector &key) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + DistributedDB::CipherPassword password; + auto status = password.SetValue(key.data(), key.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd."); + return Status::DB_ERROR; + } + DistributedDB::DBStatus dbStatus; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = kvStoreNbDelegate_->Rekey(password); + } + if (dbStatus == DistributedDB::DBStatus::OK) { + return Status::SUCCESS; + } + return Status::ERROR; +} + +Status SingleKvStoreImpl::RegisterSyncCallback(sptr callback) +{ + syncCallback_ = std::move(callback); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::UnRegisterSyncCallback() +{ + syncCallback_ = nullptr; + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::PutBatch(const std::vector &entries) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + // temporary transform. + std::vector dbEntries; + for (auto &entry : entries) { + DistributedDB::Entry dbEntry; + + std::vector keyData = Constant::TrimCopy>(entry.key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + dbEntry.key = keyData; + dbEntry.value = entry.value.Data(); + dbEntries.push_back(dbEntry); + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->PutBatch(dbEntries); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("PutBatch failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + + if (status == DistributedDB::DBStatus::EKEYREVOKED_ERROR || + status == DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR) { + ZLOGE("delegate PutBatch failed."); + return Status::SECURITY_LEVEL_ERROR; + } + + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate PutBatch failed."); + return Status::DB_ERROR; + } + + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::DeleteBatch(const std::vector &keys) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + // temporary transform. + std::vector dbKeys; + for (auto &key : keys) { + std::vector keyData = Constant::TrimCopy>(key.Data()); + if (keyData.size() == 0 || keyData.size() > Constant::MAX_KEY_LENGTH) { + ZLOGE("invalid key."); + return Status::INVALID_ARGUMENT; + } + + DistributedDB::Key keyTmp = keyData; + dbKeys.push_back(keyTmp); + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->DeleteBatch(dbKeys); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("DeleteBatch failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + + if (status == DistributedDB::DBStatus::EKEYREVOKED_ERROR || + status == DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR) { + ZLOGE("delegate DeleteBatch failed."); + return Status::SECURITY_LEVEL_ERROR; + } + + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate DeleteBatch failed."); + return Status::DB_ERROR; + } + + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::StartTransaction() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->StartTransaction(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("StartTransaction failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::Commit() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Commit(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("Commit failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::Rollback() +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::shared_lock lock(storeNbDelegateMutex_); + if (kvStoreNbDelegate_ == nullptr) { + ZLOGE("delegate is null."); + return Status::DB_ERROR; + } + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + DistributedDB::DBStatus status; + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + status = kvStoreNbDelegate_->Rollback(); + } + if (status == DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB) { + ZLOGE("Rollback failed, distributeddb need recover."); + return (Import(bundleName_) ? Status::RECOVER_SUCCESS : Status::RECOVER_FAILED); + } + if (status != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error."); + return Status::DB_ERROR; + } + + Reporter::GetInstance()->VisitStatistic()->Report({bundleName_, __FUNCTION__}); + return Status::SUCCESS; +} + +Status SingleKvStoreImpl::Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) +{ + output = nullptr; + switch (cmd) { + case KvControlCmd::SET_SYNC_PARAM: { + if (inputParam.Size() != sizeof(KvSyncParam)) { + return Status::IPC_ERROR; + } + KvSyncParam syncParam = TransferByteArrayToType(inputParam.Data()); + uint32_t allowedDelayMs = syncParam.allowedDelayMs; + if (allowedDelayMs > 0 && allowedDelayMs < KvStoreSyncManager::SYNC_MIN_DELAY_MS) { + return Status::INVALID_ARGUMENT; + } + if (allowedDelayMs > KvStoreSyncManager::SYNC_MAX_DELAY_MS) { + return Status::INVALID_ARGUMENT; + } + defaultSyncDelayMs_ = allowedDelayMs; + return Status::SUCCESS; + } + case KvControlCmd::GET_SYNC_PARAM: { + KvSyncParam syncParam{defaultSyncDelayMs_}; + output = new KvParam(TransferTypeToByteArray(syncParam)); + return Status::SUCCESS; + } + default: { + ZLOGE("control invalid command."); + return Status::ERROR; + } + } +} + +void SingleKvStoreImpl::IncreaseOpenCount() +{ + openCount_++; +} + +bool SingleKvStoreImpl::Import(const std::string &bundleName) const +{ + ZLOGI("Single KvStoreImpl Import start"); + const std::string harmonyAccountId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + auto sKey = KvStoreMetaManager::GetMetaKey(deviceAccountId_, harmonyAccountId, bundleName, storeId_, "SINGLE_KEY"); + std::vector secretKey; + bool outdated = false; + auto trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(sKey, secretKey, outdated); + MetaData metaData{0}; + metaData.kvStoreMetaData.deviceAccountId = deviceAccountId_; + metaData.kvStoreMetaData.userId = harmonyAccountId; + metaData.kvStoreMetaData.bundleName = bundleName; + metaData.kvStoreMetaData.appId = trueAppId; + metaData.kvStoreMetaData.storeId = storeId_; + metaData.secretKeyMetaData.secretKey = secretKey; + std::shared_lock lock(storeNbDelegateMutex_); + return std::make_unique()->SingleKvStoreRecover(metaData, kvStoreNbDelegate_); +} + +Status SingleKvStoreImpl::SetCapabilityEnabled(bool enabled) +{ + ZLOGD("begin."); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + std::string key; + std::string devId = DeviceKvStoreImpl::GetLocalDeviceId(); + if (devId.empty()) { + ZLOGE("get device id empty."); + return Status::ERROR; + } + + StrategyMeta params = {devId, deviceAccountId_, Constant::DEFAULT_GROUP_ID, bundleName_, storeId_}; + KvStoreMetaManager::GetInstance().GetStrategyMetaKey(params, key); + if (key.empty()) { + ZLOGE("get key empty."); + return Status::ERROR; + } + ZLOGD("end."); + return KvStoreMetaManager::GetInstance().SaveStrategyMetaEnable(key, enabled); +} + +Status SingleKvStoreImpl::SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) +{ + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + std::string key; + std::string devId = DeviceKvStoreImpl::GetLocalDeviceId(); + if (devId.empty()) { + ZLOGE("get device id empty."); + return Status::ERROR; + } + + StrategyMeta params = {devId, deviceAccountId_, Constant::DEFAULT_GROUP_ID, bundleName_, storeId_}; + KvStoreMetaManager::GetInstance().GetStrategyMetaKey(params, key); + if (key.empty()) { + ZLOGE("get key empty."); + return Status::ERROR; + } + + return KvStoreMetaManager::GetInstance().SaveStrategyMetaLabels(key, localLabels, remoteSupportLabels); +} + +Status SingleKvStoreImpl::GetSecurityLevel(SecurityLevel &securityLevel) +{ + if (kvStoreNbDelegate_ == nullptr) { + return Status::STORE_NOT_OPEN; + } + + DistributedDB::SecurityOption option; + auto status = kvStoreNbDelegate_->GetSecurityOption(option); + if (status == DistributedDB::DBStatus::NOT_SUPPORT) { + return Status::NOT_SUPPORT; + } + + if (status != DistributedDB::DBStatus::OK) { + return Status::DB_ERROR; + } + + switch (option.securityLabel) { + case DistributedDB::NOT_SET: + case DistributedDB::S0: + case DistributedDB::S1: + case DistributedDB::S2: + securityLevel = static_cast(option.securityLabel); + break; + case DistributedDB::S3: + securityLevel = option.securityFlag ? S3 : S3_EX; + break; + case DistributedDB::S4: + securityLevel = S4; + break; + default: + break; + } + return Status::SUCCESS; +} + +void SingleKvStoreImpl::OnDump(int fd) const +{ + const std::string prefix(12, ' '); + dprintf(fd, "%s------------------------------------------------------\n", prefix.c_str()); + dprintf(fd, "%sStoreID : %s\n", prefix.c_str(), storeId_.c_str()); + dprintf(fd, "%sStorePath : %s\n", prefix.c_str(), storePath_.c_str()); + + dprintf(fd, "%sOptions :\n", prefix.c_str()); + dprintf(fd, "%s backup : %d\n", prefix.c_str(), static_cast(options_.backup)); + dprintf(fd, "%s encrypt : %d\n", prefix.c_str(), static_cast(options_.encrypt)); + dprintf(fd, "%s autoSync : %d\n", prefix.c_str(), static_cast(options_.autoSync)); + dprintf(fd, "%s persistant : %d\n", prefix.c_str(), static_cast(options_.persistant)); + dprintf(fd, "%s kvStoreType : %d\n", prefix.c_str(), static_cast(options_.kvStoreType)); + dprintf(fd, "%s createIfMissing : %d\n", prefix.c_str(), static_cast(options_.createIfMissing)); + dprintf(fd, "%s schema : %s\n", prefix.c_str(), options_.schema.c_str()); +} +} // namespace OHOS::DistributedKv diff --git a/services/distributeddataservice/app/src/single_kvstore_impl.h b/services/distributeddataservice/app/src/single_kvstore_impl.h new file mode 100755 index 000000000..db7867398 --- /dev/null +++ b/services/distributeddataservice/app/src/single_kvstore_impl.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 SINGLE_KVSTORE_IMPL_H +#define SINGLE_KVSTORE_IMPL_H + +#include +#include +#include +#include +#include "flowctrl_manager/kvstore_flowctrl_manager.h" +#include "ikvstore_observer.h" +#include "ikvstore_single.h" +#include "ikvstore_sync_callback.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "kvstore_observer_impl.h" +#include "kvstore_resultset_impl.h" +#include "kvstore_sync_manager.h" +#include "inner_types.h" + +namespace OHOS::DistributedKv { +class SingleKvStoreImpl : public SingleKvStoreStub { +public: + SingleKvStoreImpl(const Options &options, const std::string &deviceAccountId, + const std::string &bundleName, const std::string &storeId, + const std::string &appDirectory, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + ~SingleKvStoreImpl(); + Status Put(const Key &key, const Value &value) override; + Status Delete(const Key &key) override; + Status Get(const Key &key, Value &value) override; + Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer) override; + Status UnSubscribeKvStore(const SubscribeType subscribeType, sptr observer) override; + Status GetEntries(const Key &prefixKey, std::vector &entries) override; + Status GetEntriesWithQuery(const std::string &query, std::vector &entries) override; + void GetResultSet(const Key &prefixKey, std::function)> callback) override; + void GetResultSetWithQuery(const std::string &query, + std::function)> callback) override; + Status GetCountWithQuery(const std::string &query, int &result) override; + Status CloseResultSet(sptr resultSet) override; + Status Sync(const std::vector &deviceIdList, const SyncMode &mode, uint32_t allowedDelayMs) override; + Status RemoveDeviceData(const std::string &device) override; + Status RegisterSyncCallback(sptr callback) override; + Status UnRegisterSyncCallback() override; + Status ReKey(const std::vector &key); + InnerStatus Close(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager); + Status ForceClose(DistributedDB::KvStoreDelegateManager *kvStoreDelegateManager); + Status MigrateKvStore(const std::string &harmonyAccountId, const std::string &kvStoreDataDir, + DistributedDB::KvStoreDelegateManager *oldDelegateMgr, + DistributedDB::KvStoreDelegateManager *&newDelegateMgr); + void IncreaseOpenCount(); + Status PutBatch(const std::vector &entries) override; + Status DeleteBatch(const std::vector &keys) override; + Status StartTransaction() override; + Status Commit() override; + Status Rollback() override; + Status Control(KvControlCmd cmd, const KvParam &inputParam, sptr &output) override; + Status SetCapabilityEnabled(bool enabled) override; + Status SetCapabilityRange(const std::vector &localLabels, + const std::vector &remoteSupportLabels) override; + Status GetSecurityLevel(SecurityLevel &securityLevel) override; + bool Import(const std::string &bundleName) const; + void GetResultSet(const Key &prefixKey, std::function)> callback, + bool deviceCoordinate); + Status SubscribeKvStore(const SubscribeType subscribeType, sptr observer, + bool deviceCoordinate); + void GetResultSetWithQuery(const std::string &query, + std::function)> callback, bool deviceCoordinate); + void OnDump(int fd) const; +private: + Status ConvertDbStatus(DistributedDB::DBStatus dbStatus); + uint32_t GetSyncDelayTime(uint32_t allowedDelayMs) const; + Status AddSync(const std::vector &deviceIdList, const SyncMode &mode, uint32_t delayMs); + Status RemoveAllSyncOperation(); + void DoSyncComplete(const std::map &devicesSyncResult); + Status DoSync(const std::vector &deviceIdList, const SyncMode &mode, + const KvStoreSyncManager::SyncEnd &syncEnd); + Status AddAutoSync(); + Status DoAutoSync(const KvStoreSyncManager::SyncEnd &); + Status RebuildKvStoreObserver(DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + Status RebuildKvStoreResultSet(); + int ConvertToDbObserverMode(SubscribeType subscribeType) const; + + // kvstore options. + const Options options_; + // kvstore cipherKey. + const std::vector cipherKey_; + // deviceAccount id get from service + std::string deviceAccountId_; + // appId get from PMS. + const std::string bundleName_; + // kvstore name. + const std::string storeId_; + // kvstore absolute path in distributeddatamgr. + const std::string storePath_; + // for top-app, 0 means synchronization immediately. for others, 0 means 1000ms. + uint32_t defaultSyncDelayMs_{ 0 }; + std::atomic_uint32_t waitingSyncCount_{ 0 }; + std::atomic_uint32_t waitingAutoSyncCount_{ 0 }; + std::atomic_uint32_t syncRetries_{ 0 }; + std::vector lastSyncDeviceIdList_{}; + SyncMode lastSyncMode_{ SyncMode::PULL }; + uint32_t lastSyncDelayMs_{ 0 }; + + // distributeddb is responsible for free kvStoreNbDelegate_, + // (destruct will be done while calling CloseKvStore in KvStoreDelegateManager) + // so DO NOT free it in SingleKvStoreImpl's destructor. + mutable std::shared_mutex storeNbDelegateMutex_{}; + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate_; + std::mutex observerMapMutex_; + std::map observerMap_; + std::mutex storeResultSetMutex_; + std::map> storeResultSetMap_; + sptr syncCallback_; + int openCount_; + + // flowControl + KvStoreFlowCtrlManager flowCtrlManager_; + static constexpr int BURST_CAPACITY = 1000; + static constexpr int SUSTAINED_CAPACITY = 10000; +}; +} // namespace OHOS::DistributedKv +#endif // SINGLE_KVSTORE_IMPL_H diff --git a/services/distributeddataservice/app/src/uninstaller/BUILD.gn b/services/distributeddataservice/app/src/uninstaller/BUILD.gn new file mode 100755 index 000000000..f57f3316a --- /dev/null +++ b/services/distributeddataservice/app/src/uninstaller/BUILD.gn @@ -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. + +import("//build/ohos.gni") + +ohos_static_library("distributeddata_uninstaller_static") { + sources = [ + "uninstaller.cpp", + "uninstaller_impl.cpp", + ] + + include_dirs = [ + "../../../adapter/include/account", + "../../src", + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//third_party/json/single_include", + "//utils/native/base/include", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//utils/native/base:utils", + ] + + external_deps = [ + "appexecfwk_standard:appexecfwk_base", + + # "ces:libcommonevent", + "aafwk_standard:base", + "aafwk_standard:intent", + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] +} diff --git a/services/distributeddataservice/app/src/uninstaller/uninstaller.cpp b/services/distributeddataservice/app/src/uninstaller/uninstaller.cpp new file mode 100755 index 000000000..5cdf855f9 --- /dev/null +++ b/services/distributeddataservice/app/src/uninstaller/uninstaller.cpp @@ -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. + */ + +#include "uninstaller.h" +#include "uninstaller_impl.h" + +namespace OHOS::DistributedKv { +Uninstaller &Uninstaller::GetInstance() +{ + static UninstallerImpl uninstaller; + return uninstaller; +} +} diff --git a/services/distributeddataservice/app/src/uninstaller/uninstaller.h b/services/distributeddataservice/app/src/uninstaller/uninstaller.h new file mode 100755 index 000000000..9fdcb9d6c --- /dev/null +++ b/services/distributeddataservice/app/src/uninstaller/uninstaller.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 DISTRIBUTEDDATAMGR_UNINSTALLER_H +#define DISTRIBUTEDDATAMGR_UNINSTALLER_H + +#include +#include + +#include "ikvstore_data_service.h" +#include "visibility.h" + +namespace OHOS::DistributedKv { +class Uninstaller { +public: + KVSTORE_API virtual Status Init(IKvStoreDataService *kvStoreDataService) = 0; + KVSTORE_API virtual ~Uninstaller() {}; + KVSTORE_API static Uninstaller &GetInstance(); +}; +} +#endif // DISTRIBUTEDDATAMGR_UNINSTALLER_H diff --git a/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.cpp b/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.cpp new file mode 100755 index 000000000..d8c7459d3 --- /dev/null +++ b/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.cpp @@ -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. + */ + +#define LOG_TAG "UninstallerImpl" + +#include "uninstaller_impl.h" + +namespace OHOS::DistributedKv { +const std::string PACKAGE_SCHEME = "package"; +const std::string SCHEME_SPLIT = ":"; +const std::string EXTRA_REPLACING = "intent.extra.REPLACING"; + +UninstallerImpl::~UninstallerImpl() +{ + +} + +Status UninstallerImpl::Init(IKvStoreDataService *kvStoreDataService) +{ + return Status::ERROR; +} +} diff --git a/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.h b/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.h new file mode 100755 index 000000000..5a47a468a --- /dev/null +++ b/services/distributeddataservice/app/src/uninstaller/uninstaller_impl.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 DISTRIBUTEDDATAMGR_UNINSTALLER_IMPL_H +#define DISTRIBUTEDDATAMGR_UNINSTALLER_IMPL_H + +#include "uninstaller.h" + +namespace OHOS::DistributedKv { +class UninstallerImpl : public Uninstaller { +public: + UninstallerImpl() {}; + + ~UninstallerImpl(); + + Status Init(IKvStoreDataService *kvStoreDataService) override; +}; +} +#endif // DISTRIBUTEDDATAMGR_UNINSTALLER_IMPL_H diff --git a/services/distributeddataservice/app/test/BUILD.gn b/services/distributeddataservice/app/test/BUILD.gn new file mode 100755 index 000000000..532e5ca46 --- /dev/null +++ b/services/distributeddataservice/app/test/BUILD.gn @@ -0,0 +1,404 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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/distributeddataservice" + +############################################################################### +config("module_private_config") { + visibility = [ ":*" ] + include_dirs = [ + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/include", + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/innerkitsimpl/distributeddatafwk/src", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/permission", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/account", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/broadcaster", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/utils", + "//utils/native/base/include", + "//utils/system/safwk/native/include", + "../include", + "../src", + "../src/uninstaller", + "../src/flowctrl_manager", + "../../../../interfaces/innerkits/distributeddata", + "//third_party/json/single_include", + ] +} + +ohos_unittest("KvStoreImplLogicalIsolationTest") { + module_out_path = module_output_path + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_app_manager.cpp", + "../src/kvstore_data_service.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + "unittest/kvstore_impl_logical_isolation_test.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreImplPhysicalIsolationTest") { + module_out_path = module_output_path + + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_app_manager.cpp", + "../src/kvstore_data_service.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + "unittest/kvstore_impl_physical_isolation_test.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreDataServiceTest") { + module_out_path = module_output_path + + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_app_manager.cpp", + "../src/kvstore_data_service.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + "unittest/kvstore_data_service_test.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreBackupTest") { + module_out_path = module_output_path + + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + "unittest/kvstore_app_manager.cpp", + "unittest/kvstore_backup_test.cpp", + "unittest/kvstore_data_service.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] + + resource_config_file = "//foundation/distributeddatamgr/distributeddatamgr/test/resource/distributeddataservice/ohos_test.xml" +} + +ohos_unittest("KvStoreFlowCtrlManagerTest") { + module_out_path = module_output_path + + sources = [ "unittest/kvstore_flowctrl_manager_test.cpp" ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreSyncManagerTest") { + module_out_path = module_output_path + + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_app_manager.cpp", + "../src/kvstore_data_service.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + ] + + configs = [ ":module_private_config" ] + + external_deps = [ + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +ohos_unittest("KvStoreUninstallerTest") { + module_out_path = module_output_path + + sources = [ + "../src/backup_handler.cpp", + "../src/device_change_listener_impl.cpp", + "../src/device_kvstore_impl.cpp", + "../src/device_kvstore_observer_impl.cpp", + "../src/device_kvstore_resultset_impl.cpp", + "../src/kvstore_account_observer.cpp", + "../src/kvstore_app_accessor.cpp", + "../src/kvstore_app_manager.cpp", + "../src/kvstore_data_service.cpp", + "../src/kvstore_impl.cpp", + "../src/kvstore_meta_manager.cpp", + "../src/kvstore_observer_impl.cpp", + "../src/kvstore_resultset_impl.cpp", + "../src/kvstore_snapshot_impl.cpp", + "../src/kvstore_sync_manager.cpp", + "../src/kvstore_user_manager.cpp", + "../src/query_helper.cpp", + "../src/single_kvstore_impl.cpp", + "unittest/uninstaller_test.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/:distributeddata", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/account:distributeddata_account_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/broadcaster:distributeddata_broadcaster_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/permission:distributeddata_permission_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/utils:distributeddata_utils_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/flowctrl_manager:distributeddata_flowctrl_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app/src/uninstaller:distributeddata_uninstaller_static", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] + + external_deps = [ + "aafwk_standard:base", + "aafwk_standard:intent", + "battery_manager_native:batterysrv_client", + "hiviewdfx_hilog_native:libhilog", + "huks_standard:libhukssdk", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_L2:samgr_proxy", + ] +} + +############################################################################### + +group("unittest") { + testonly = true + deps = [] + + deps += [ + ":KvStoreBackupTest", + ":KvStoreDataServiceTest", + ":KvStoreFlowCtrlManagerTest", + ":KvStoreImplLogicalIsolationTest", + ":KvStoreImplPhysicalIsolationTest", + ":KvStoreUninstallerTest", + ] +} + +############################################################################### +group("moduletest") { + testonly = true + deps = [ + "//third_party/googletest:gmock", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + ] + + deps += [ + #":DistributedDataAccountEventModuleTest", + #":DistributedDataFlowCtrlManagerTest", + ] +} +############################################################################### diff --git a/services/distributeddataservice/app/test/moduletest/distributeddata_account_event_test.cpp b/services/distributeddataservice/app/test/moduletest/distributeddata_account_event_test.cpp new file mode 100644 index 000000000..34578d2ef --- /dev/null +++ b/services/distributeddataservice/app/test/moduletest/distributeddata_account_event_test.cpp @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kvstore_account_observer.h" +#include "kvstore_observer_client.h" +#include "kvstore_data_service.h" +#include "kvstore_impl.h" +#include "refbase.h" +#include "types.h" +#include +#include +#include +#include +#include "directory_ex.h" +#include "constant.h" +#include "common_event_subscriber.h" +#include "common_event_support.h" +#include "common_event_define.h" +#include "common_event_manager.h" +#include "ohos/aafwk/content/intent.h" +#include "ohos_account_kits.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; +using namespace Notification; + +static const int SYSTEM_USER_ID = 1000; + +static const int WAIT_TIME_FOR_ACCOUNT_OPERATION = 2; // indicates the wait time in seconds + +class DistributedDataAccountEventTest : public testing::Test { +public: + static void SetUpTestCase(); + static void ChangeUser(int uid); + static void TearDownTestCase(); + static void HarmonyAccountLogin(); + static void HarmonyAccountLogout(); + static void HarmonyAccountDelete(); +}; + +void DistributedDataAccountEventTest::SetUpTestCase() +{ + DistributedDataAccountEventTest::ChangeUser(SYSTEM_USER_ID); +} + +void DistributedDataAccountEventTest::TearDownTestCase() +{ + DistributedDataAccountEventTest::HarmonyAccountDelete(); +} + +void DistributedDataAccountEventTest::HarmonyAccountLogin() +{ + sptr intent = new AAFwk::Intent(); + intent->SetAction(CommonEventSupport::COMMON_EVENT_HWID_LOGIN); + sptr event = new CommonEventData(intent); + sptr publishInfo = new CommonEventPublishInfo(); + auto err = CommonEventManager::GetInstance().PublishCommonEventData(event, publishInfo, nullptr); + EXPECT_EQ(ERR_OK, err); + sleep(WAIT_TIME_FOR_ACCOUNT_OPERATION); +} + +void DistributedDataAccountEventTest::HarmonyAccountLogout() +{ + sptr intent = new AAFwk::Intent(); + intent->SetAction(CommonEventSupport::COMMON_EVENT_HWID_LOGOUT); + sptr event = new CommonEventData(intent); + sptr publishInfo = new CommonEventPublishInfo(); + auto err = CommonEventManager::GetInstance().PublishCommonEventData(event, publishInfo, nullptr); + EXPECT_EQ(ERR_OK, err); + sleep(WAIT_TIME_FOR_ACCOUNT_OPERATION); +} + +void DistributedDataAccountEventTest::HarmonyAccountDelete() +{ + sptr intent = new AAFwk::Intent(); + intent->SetAction(CommonEventSupport::COMMON_EVENT_HWID_TOKEN_INVALID); + sptr event = new CommonEventData(intent); + sptr publishInfo = new CommonEventPublishInfo(); + auto err = CommonEventManager::GetInstance().PublishCommonEventData(event, publishInfo, nullptr); + EXPECT_EQ(ERR_OK, err); + sleep(WAIT_TIME_FOR_ACCOUNT_OPERATION); +} + +void DistributedDataAccountEventTest::ChangeUser(int uid) +{ + if (setgid(uid)) { + std::cout << "error to set gid " << uid << "errno is " << errno << std::endl; + } + + if (setuid(uid)) { + std::cout << "error to set uid " << uid << "errno is " << errno << std::endl; + } +} + +/** + * @tc.name: KvStore data storage path verify when get KvStore with default device account and harmony account logout. + * @tc.desc: Verify that the KvStore data storage path is consistent with the path spliced by distributedDB interface. + * with default device account and harmony account logout. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPSE5 + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_001, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore001"; + std::string hashedStoreId; + + // Step1. Splice kvStore data storage expected path by distributedDB hash interface and clear directory. + DistributedDB::DBStatus dbStatus = + DistributedDB::KvStoreDelegateManager::GetDatabaseDir(storeId.storeId, hashedStoreId); + EXPECT_EQ(dbStatus, DistributedDB::OK) << "Get data directory name from DB failed."; + std::string appDataStoragePath; + appDataStoragePath = KvStoreAppManager::GetDataStoragePath("0", appId.appId, KvStoreAppManager::PATH_CE); + const std::string kvStoreDataStorageExpectPath = Constant::Concatenate( + { appDataStoragePath, "/", appId.appId, "/", hashedStoreId }); + DIR *dir = opendir(kvStoreDataStorageExpectPath.c_str()); + if (dir != nullptr) { + ForceRemoveDirectory(kvStoreDataStorageExpectPath); + closedir(dir); + } + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + EXPECT_EQ(dir, nullptr) << "KvStore data storage directory was not cleared successfully."; + + // Step2. Get KvStore and check the created data storage path is consistent with the expected directory. + KvStoreDataService kvStoreDataService; + sptr iKvStoreImplPtr; + Status status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplPtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(iKvStoreImplPtr, nullptr) << "GetKvStore executed failed"; + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_NE(dir, nullptr) << "KvStore data storage directory created is not consistent with the expected."; + closedir(dir); +} + +/** + * @tc.name: Re-operate exist KvStore successfully verify when harmony account login/logout. + * @tc.desc: Verify that after harmony account login/logout, re-operate exist KvStore successfully. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPSE7 + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_002, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore002"; + std::string hashedStoreId; + + // Step1. Get KvStore with specified appId and storeId when harmony account logout. + KvStoreDataService kvStoreDataService; + sptr iKvStoreImplPtr; + Status status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplPtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(iKvStoreImplPtr, nullptr) << "GetKvStore executed failed"; + + Key key = "Von"; + Value value = "Leon"; + status = iKvStoreImplPtr->Put(key, value); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to KvStore return wrong status"; + + // Step2. Get KvStoreSnapshot and verify get value from KvStore correctly. + sptr kvStoreObserverClient = + new KvStoreObserverClient(storeId, SubscribeType::SUBSCRIBE_TYPE_ALL, nullptr, KvStoreType::MULTI_VERSION); + sptr snapshotProxyTmp; + auto snapshotCallbackFunction = [&](Status statusTmp, sptr snapshotProxy) { + status = statusTmp; + snapshotProxyTmp = snapshotProxy; + }; + iKvStoreImplPtr->GetKvStoreSnapshot(kvStoreObserverClient, snapshotCallbackFunction); + Value getValue = ""; + snapshotProxyTmp->Get(key, getValue); + EXPECT_EQ(getValue, value) << "Get string from KvStore not equal to PUT"; + + sleep(WAIT_TIME_FOR_ACCOUNT_OPERATION); + // Step3. Harmony account login + DistributedDataAccountEventTest::HarmonyAccountLogin(); + + // Step4. Verify get value from KvStore and put string to KvStore with last KvStoreSnapShot correctly after LOGIN. + getValue.Clear(); + snapshotProxyTmp->Get(key, getValue); + EXPECT_EQ(getValue, value) << "Get string from KvStore not equal to PUT after LOGIN"; + + Key key2 = "Von2"; + Value value2 = "Leon2"; + status = iKvStoreImplPtr->Put(key2, value2); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to KvStore return wrong status"; + + sptr kvStoreObserverClient1 = + new KvStoreObserverClient(storeId, SubscribeType::SUBSCRIBE_TYPE_ALL, nullptr, KvStoreType::MULTI_VERSION); + sptr snapshotProxyTmp1; + auto snapshotCallbackFunction1 = [&](Status statusTmp, sptr snapshotProxy) { + status = statusTmp; + snapshotProxyTmp1 = snapshotProxy; + }; + iKvStoreImplPtr->GetKvStoreSnapshot(kvStoreObserverClient1, snapshotCallbackFunction1); + + getValue.Clear(); + snapshotProxyTmp1->Get(key2, getValue); + EXPECT_EQ(getValue, value2) << "Get string from KvStore not equal to PUT after LOGIN"; + + // Step6. Harmony account logout + DistributedDataAccountEventTest::HarmonyAccountLogout(); + + // Step7. Verify get value from KvStore and put string to KvStore with last KvStoreSnapShot correctly after LOGOUT. + getValue.Clear(); + snapshotProxyTmp->Get(key, getValue); + EXPECT_EQ(getValue, value) << ", Get string from KvStore not equal to PUT after LOGOUT"; + + Key key3 = "Von3"; + Value value3 = "Leon3"; + status = iKvStoreImplPtr->Put(key3, value3); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to KvStore return wrong status"; + + sptr kvStoreObserverClient2 = + new KvStoreObserverClient(storeId, SubscribeType::SUBSCRIBE_TYPE_ALL, nullptr, KvStoreType::MULTI_VERSION); + sptr snapshotProxyTmp2; + auto snapshotCallbackFunction2 = [&](Status statusTmp, sptr snapshotProxy) { + status = statusTmp; + snapshotProxyTmp2 = snapshotProxy; + }; + iKvStoreImplPtr->GetKvStoreSnapshot(kvStoreObserverClient2, snapshotCallbackFunction2); + getValue.Clear(); + snapshotProxyTmp2->Get(key3, getValue); + EXPECT_EQ(getValue, value3) << "Get string from KvStore not equal to PUT after LOGOUT"; + + iKvStoreImplPtr->ReleaseKvStoreSnapshot(snapshotProxyTmp); + iKvStoreImplPtr->ReleaseKvStoreSnapshot(snapshotProxyTmp1); + iKvStoreImplPtr->ReleaseKvStoreSnapshot(snapshotProxyTmp2); + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status after harmony account logout"; + + // Step7. Verify that when harmony account logout and in the situation that the exist KvStore has been closed, + // re-get exist KvStore successfully and the KvStoreImplPtr not equal with the one before harmony account logout. + sptr iKvStoreImplLogoutPtr; + status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplLogoutPtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status after harmony account logout"; + EXPECT_NE(iKvStoreImplLogoutPtr, nullptr) << "GetKvStore executed failed after harmony account logout"; + EXPECT_NE(iKvStoreImplPtr, iKvStoreImplLogoutPtr) << "kvStoreImpl NE fail after harmony account logout"; + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status after harmony account logout"; +} + +/** + * @tc.name: KvStore data storage path verify when the ownership of distributed data set to ACCOUNT. + * @tc.desc: Verify that in the situation that distributed data ownership set to ACCOUNT, get KvStore successfully + * and verify that the KvStore data storage path is consistent with the path spliced by distributedDB interface. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPTQ8 + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_003, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore003"; + std::string hashedStoreId; + KvStoreDataService kvStoreDataService; + + // Step1. Set distributed data ownership to ACCOUNT. + options.dataOwnership = false; + + // Step2. Splice kvStore data storage expected path by distributedDB hash interface and clear directory. + DistributedDB::DBStatus dbStatus = DistributedDB::KvStoreDelegateManager::GetDatabaseDir( + storeId.storeId, appId.appId, AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), hashedStoreId); + EXPECT_EQ(dbStatus, DistributedDB::OK) << "Get data directory name from DB failed."; + + std::string appDataStoragePath; + appDataStoragePath = KvStoreAppManager::GetDataStoragePath("0", appId.appId, KvStoreAppManager::PATH_CE); + const std::string kvStoreDataStorageExpectPath = Constant::Concatenate( + { appDataStoragePath, "/", appId.appId, "/", hashedStoreId }); + DIR *dir = opendir(kvStoreDataStorageExpectPath.c_str()); + if (dir != nullptr) { + ForceRemoveDirectory(kvStoreDataStorageExpectPath); + closedir(dir); + } + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + EXPECT_EQ(dir, nullptr) << "KvStore data storage directory was not cleared successfully."; + + // Step2. Get KvStore and check the created data storage path is consistent with the expected directory. + sptr iKvStoreImplPtr; + Status status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplPtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(iKvStoreImplPtr, nullptr) << "GetKvStore executed failed"; + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_NE(dir, nullptr) << "KvStore data storage directory created is not consistent with the expected."; + closedir(dir); +} + +/** + * @tc.name: System upgrade kvStore data migration verify when the data ownership changed from ACCUNT to DEVICE. + * @tc.desc: Verify that in the situation that distributed data ownership set to ACCOUNT, get KvStore successfully + * then set data ownership set to DEVICE, harmony account login, re-get kvStore successfully and verify that the + * KvStore data storage path changed to the path spliced by distributedDB interface. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPSDH + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_004, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore004"; + std::string hashedStoreId; + KvStoreDataService kvStoreDataService; + + // Step1. Set distributed data ownership to ACCOUNT. + options.dataOwnership = false; + + // Step2. Splice kvStore data storage expected path by distributedDB hash interface and clear directory. + DistributedDB::DBStatus dbStatus = DistributedDB::KvStoreDelegateManager::GetDatabaseDir( + storeId.storeId, appId.appId, AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), hashedStoreId); + EXPECT_EQ(dbStatus, DistributedDB::OK) << "Get data directory name from DB failed."; + + std::string appDataStoragePath; + appDataStoragePath = KvStoreAppManager::GetDataStoragePath("0", appId.appId, KvStoreAppManager::PATH_CE); + std::string kvStoreDataStorageExpectPath = Constant::Concatenate( + { appDataStoragePath, "/", appId.appId, "/", hashedStoreId }); + DIR *dir = opendir(kvStoreDataStorageExpectPath.c_str()); + if (dir != nullptr) { + ForceRemoveDirectory(kvStoreDataStorageExpectPath); + closedir(dir); + } + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + EXPECT_EQ(dir, nullptr) << "KvStore data storage directory was not cleared successfully."; + + // Step2. Get KvStore. + sptr iKvStoreImplPtr; + Status status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplPtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(iKvStoreImplPtr, nullptr) << "GetKvStore executed failed"; + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status"; + + // Step3. Set distributed data ownership to DEVICE. + options.dataOwnership = true; + + // Step4. Harmony account login + DistributedDataAccountEventTest::HarmonyAccountLogin(); + + // Step5. Re-Get KvStore and verify the data storage path has been changed to only store id hashed path. + sptr iKvStoreImplRePtr; + status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplRePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status when harmony account login"; + EXPECT_NE(iKvStoreImplRePtr, nullptr) << "GetKvStore executed failed when harmony account login"; + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status when harmony account login"; + + hashedStoreId.clear(); + dbStatus = DistributedDB::KvStoreDelegateManager::GetDatabaseDir(storeId.storeId, hashedStoreId); + EXPECT_EQ(dbStatus, DistributedDB::OK) << "Get data directory name from DB failed."; + kvStoreDataStorageExpectPath.clear(); + kvStoreDataStorageExpectPath = Constant::Concatenate({ appDataStoragePath, "/", appId.appId, "/", hashedStoreId }); + dir = opendir(kvStoreDataStorageExpectPath.c_str()); + EXPECT_NE(dir, nullptr) << "KvStore data storage directory was not changed correctly."; + closedir(dir); +} + +/** + * @tc.name: APP get KvStore failed verify when DDS is processing harmony account login event. + * @tc.desc: Verify that in the situation that DDS is processing harmony account login event, + * APP will get KvStore failed. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPSE6 + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_005, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore005"; + std::string hashedStoreId; + KvStoreDataService kvStoreDataService; + + // Step1. Harmony account login. + DistributedDataAccountEventTest::HarmonyAccountLogin(); + + // Step2. Set DDS status to processing harmony account login event. + g_kvStoreAccountEventStatus = 1; + + // Step3. Get KvStore. + sptr iKvStoreImplPtr; + Status status = kvStoreDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { iKvStoreImplPtr = std::move(kvStore); }); + // Caution: When the function opened, this verify should be EQ + EXPECT_NE(status, Status::SYSTEM_ACCOUNT_EVENT_PROCESSING) << "GetKvStore return unexpectedstatus"; + EXPECT_NE(iKvStoreImplPtr, nullptr) << "GetKvStore executed with unexpectedresult"; + + // Step4. Restore DDS status to finish harmony account login event process. + g_kvStoreAccountEventStatus = 0; +} + +/** + * @tc.name: Re-get exist SingleKvStore successfully verify when harmony account login/logout. + * @tc.desc: Verify that after harmony account login/logout, re-operate exist SingleKvStore successfully. + * @tc.type: FUNC + * @tc.require: SR000DOH0F AR000DPSE7 + * @tc.author: FengLin + */ +HWTEST_F(DistributedDataAccountEventTest, GetKvStore_DefaultDeviceAccount_006, TestSize.Level3) +{ + Options options; + options.createIfMissing = true; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::SINGLE_VERSION; + + AppId appId; + appId.appId = "com.ohos.distributeddata.accountmsttest"; + StoreId storeId; + storeId.storeId = "AccountEventStore006"; + std::string hashedStoreId; + + // Step1. Get KvStore with specified appId and storeId when harmony account logout. + KvStoreDataService kvStoreDataService; + sptr iSingleKvStorePtr; + Status status = kvStoreDataService.GetSingleKvStore(options, appId, storeId, + [&](sptr singleKvStore) { iSingleKvStorePtr = std::move(singleKvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetSingleKvStore return wrong status"; + EXPECT_NE(iSingleKvStorePtr, nullptr) << "GetSingleKvStore executed failed"; + + Key key = "Von"; + Value value = "Leon"; + status = iSingleKvStorePtr->Put(key, value); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to SingleKvStore return wrong status"; + Value getValue = ""; + iSingleKvStorePtr->Get(key, getValue); + EXPECT_EQ(getValue, value) << "Get string from SingleKvStore not equal to PUT"; + + // Step2. Get ResultSet and verify get entry from SingleKvStore correctly. + sptr iKvStoreResultSet; + auto resultSetCallbackFunction = [&](Status statusTmp, sptr iKvStoreResultSetTmp) { + status = statusTmp; + iKvStoreResultSet = iKvStoreResultSetTmp; + }; + Key prefixKey = "Von"; + Entry entry; + iSingleKvStorePtr->GetResultSet(prefixKey, resultSetCallbackFunction); + EXPECT_EQ(status, Status::SUCCESS) << "Get resultset from SingleKvStore return wrong status"; + iKvStoreResultSet->MoveToNext(); + status = iKvStoreResultSet->GetEntry(entry); + EXPECT_EQ(status, Status::SUCCESS) << "Get entry from ResultSet return wrong status"; + EXPECT_EQ(entry.key, key) << "Get entry key from SingleKvStore not equal to PUT"; + EXPECT_EQ(entry.value, value) << "Get entry value from SingleKvStore not equal to PUT"; + + sleep(WAIT_TIME_FOR_ACCOUNT_OPERATION); + // Step3. Harmony account login + DistributedDataAccountEventTest::HarmonyAccountLogin(); + + // Step4. Verify get value from SingleKvStore and put to SingleKvStore with last resultset correctly after LOGIN. + getValue.Clear(); + iSingleKvStorePtr->Get(key, getValue); + EXPECT_EQ(getValue, value) << "Get string from SingleKvStore not equal to PUT"; + + Entry entry2; + iKvStoreResultSet->GetEntry(entry2); + EXPECT_EQ(entry2.key, key) << "Get entry key from SingleKvStore not equal to PUT"; + EXPECT_EQ(entry2.value, value) << "Get entry value from SingleKvStore not equal to PUT"; + + Key key2 = "Von2"; + Value value2 = "Leon2"; + status = iSingleKvStorePtr->Put(key2, value2); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to SingleKvStore return wrong status"; + getValue.Clear(); + iSingleKvStorePtr->Get(key2, getValue); + EXPECT_EQ(getValue, value2) << "Get string from SingleKvStore not equal to PUT after LOGIN"; + + Entry entry3; + iSingleKvStorePtr->CloseResultSet(iKvStoreResultSet); + iSingleKvStorePtr->GetResultSet(prefixKey, resultSetCallbackFunction); + EXPECT_EQ(status, Status::SUCCESS) << "Get resultset from SingleKvStore return wrong status"; + iKvStoreResultSet->MoveToNext(); + status = iKvStoreResultSet->GetEntry(entry3); + EXPECT_EQ(status, Status::SUCCESS) << "Get entry from SingleKvStore return wrong status"; + EXPECT_EQ(entry3.key, key) << "Get entry key from SingleKvStore not equal to PUT"; + EXPECT_EQ(entry3.value, value) << "Get entry value from SingleKvStore not equal to PUT"; + + // Step6. Harmony account logout + DistributedDataAccountEventTest::HarmonyAccountLogout(); + + // Step7. Verify get value from SingleKvStore and put to SingleKvStore with last resultset correctly after LOGOUT. + getValue.Clear(); + iSingleKvStorePtr->Get(key, getValue); + EXPECT_EQ(getValue, value) << "Get string from SingleKvStore not equal to PUT"; + + Entry entry4; + iKvStoreResultSet->GetEntry(entry4); + EXPECT_EQ(entry4.key, key) << "Get entry key from SingleKvStore not equal to PUT"; + EXPECT_EQ(entry4.value, value) << "Get entry value from SingleKvStore not equal to PUT"; + + Key key3 = "Von3"; + Value value3 = "Leon3"; + status = iSingleKvStorePtr->Put(key3, value3); + EXPECT_EQ(status, Status::SUCCESS) << "PUT string to SingleKvStore return wrong status"; + getValue.Clear(); + iSingleKvStorePtr->Get(key3, getValue); + EXPECT_EQ(getValue, value3) << "Get string from SingleKvStore not equal to PUT after LOGIN"; + + Entry entry5; + iSingleKvStorePtr->GetResultSet(prefixKey, resultSetCallbackFunction); + iKvStoreResultSet->MoveToNext(); + iKvStoreResultSet->GetEntry(entry5); + EXPECT_EQ(entry5.key, key) << "Get entry key from SingleKvStore not equal to PUT"; + EXPECT_EQ(entry5.value, value) << "Get entry value from SingleKvStore not equal to PUT"; + + iSingleKvStorePtr->CloseResultSet(iKvStoreResultSet); + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status after harmony account logout"; + + // Step8. Verify that when harmony account logout and in the situation that the exist KvStore has been closed, + // re-get exist KvStore successfully and the iSingleKvStorePtr not equal with the one before harmony account logout. + sptr iSingleKvStoreLogoutPtr; + status = kvStoreDataService.GetSingleKvStore(options, appId, storeId, + [&](sptr singleKvStore) { iSingleKvStoreLogoutPtr = std::move(singleKvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetSingleKvStore return wrong status after harmony account logout"; + EXPECT_NE(iSingleKvStoreLogoutPtr, nullptr) << "GetSingleKvStore executed failed after harmony account logout"; + EXPECT_NE(iSingleKvStorePtr, iSingleKvStoreLogoutPtr) << "iSingleKvStorePtr NE fail after harmony account logout"; + status = kvStoreDataService.CloseKvStore(appId, storeId); + EXPECT_EQ(status, Status::SUCCESS) << "CloseKvStore return wrong status after harmony account logout"; +} \ No newline at end of file diff --git a/services/distributeddataservice/app/test/unittest/kvstore_app_manager.cpp b/services/distributeddataservice/app/test/unittest/kvstore_app_manager.cpp new file mode 100755 index 000000000..a5cdb6c9b --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_app_manager.cpp @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreAppManager" + +#include "kvstore_app_manager.h" +#include +#include +#include +#include +#include +#include "account_delegate.h" +#include "broadcast_sender.h" +#include "constant.h" +#include "directory_utils.h" +#include "device_kvstore_impl.h" +#include "ikvstore.h" +#include "kv_store_delegate.h" +#include "kvstore_app_accessor.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "process_communicator_impl.h" +#include "permission_validator.h" +#include "reporter.h" +#include "types.h" + +namespace OHOS { +namespace DistributedKv { +KvStoreAppManager::KvStoreAppManager(const std::string &bundleName, const std::string &deviceAccountId) + : bundleName_(bundleName), deviceAccountId_(deviceAccountId), flowCtrlManager_(BURST_CAPACITY, SUSTAINED_CAPACITY) +{ + ZLOGI("begin."); + GetDelegateManager(PATH_DE); + GetDelegateManager(PATH_CE); +} + +KvStoreAppManager::~KvStoreAppManager() +{ + ZLOGD("begin."); + stores_[PATH_DE].clear(); + stores_[PATH_CE].clear(); + + { + std::lock_guard guard(delegateMutex_); + delete delegateManagers_[PATH_DE]; + delete delegateManagers_[PATH_CE]; + delegateManagers_[PATH_DE] = nullptr; + delegateManagers_[PATH_CE] = nullptr; + } +} + +Status KvStoreAppManager::ConvertErrorStatus(DistributedDB::DBStatus dbStatus, bool createIfMissing) +{ + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate return error: %d.", static_cast(dbStatus)); + switch (dbStatus) { + case DistributedDB::DBStatus::INVALID_PASSWD_OR_CORRUPTED_DB: + return Status::CRYPT_ERROR; + case DistributedDB::DBStatus::SCHEMA_MISMATCH: + return Status::SCHEMA_MISMATCH; + case DistributedDB::DBStatus::INVALID_SCHEMA: + return Status::INVALID_SCHEMA; + case DistributedDB::DBStatus::NOT_SUPPORT: + return Status::NOT_SUPPORT; + case DistributedDB::DBStatus::EKEYREVOKED_ERROR: // fallthrough + case DistributedDB::DBStatus::SECURITY_OPTION_CHECK_ERROR: + return Status::SECURITY_LEVEL_ERROR; + default: + break; + } + if (createIfMissing) { + return Status::DB_ERROR; + } else { + return Status::STORE_NOT_FOUND; + } + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::GetKvStore(const Options &options, const std::string &storeId, + const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin"); + PathType type = ConvertPathType(bundleName_, options.securityLevel); + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManagers[%d] is nullptr.", type); + callback(nullptr); + return Status::ILLEGAL_STATE; + } + + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + std::lock_guard lg(storeMutex_); + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + sptr kvStoreImpl = it->second; + ZLOGI("find store in map refcount: %d.", kvStoreImpl->GetSptrRefCount()); + static_cast(kvStoreImpl.GetRefPtr())->IncreaseOpenCount(); + callback(std::move(kvStoreImpl)); + return Status::SUCCESS; + } + + if ((GetTotalKvStoreNum()) >= static_cast(Constant::MAX_OPEN_KVSTORES)) { + ZLOGE("limit %d KvStores can be opened.", Constant::MAX_OPEN_KVSTORES); + callback(nullptr); + return Status::ERROR; + } + + DistributedDB::KvStoreDelegate::Option dbOption; + auto status = InitDbOption(options, cipherKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitDbOption failed."); + callback(nullptr); + return status; + } + + DistributedDB::KvStoreDelegate *storeDelegate = nullptr; + DistributedDB::DBStatus dbStatusTmp; + delegateManager->GetKvStore(storeId, dbOption, + [&storeDelegate, &dbStatusTmp](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreDelegate *delegate) { + storeDelegate = delegate; + dbStatusTmp = dbStatus; + }); + + if (storeDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr, status:%d.", static_cast(dbStatusTmp)); + callback(nullptr); + return ConvertErrorStatus(dbStatusTmp, options.createIfMissing); + } + + ZLOGD("get delegate"); + sptr store = new (std::nothrow)KvStoreImpl(options, deviceAccountId_, bundleName_, + storeId, GetDbDir(options), storeDelegate); + if (store == nullptr) { + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + auto result = stores_[type].emplace(storeId, store); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + + sptr kvStoreImpl = result.first->second; + ZLOGD("after emplace refcount: %d", kvStoreImpl->GetSptrRefCount()); + callback(std::move(kvStoreImpl)); + return Status::SUCCESS; +} + +Status KvStoreAppManager::GetKvStore(const Options &options, const std::string &storeId, + const std::vector &cipherKey, + std::function)> callback) +{ + ZLOGI("begin"); + PathType type = ConvertPathType(bundleName_, options.securityLevel); + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManagers[%d] is nullptr.", type); + callback(nullptr); + return Status::ILLEGAL_STATE; + } + + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + std::lock_guard lg(storeMutex_); + auto it = singleStores_[type].find(storeId); + if (it != singleStores_[type].end()) { + sptr singleKvStoreImpl = it->second; + ZLOGI("find store in map refcount: %d.", singleKvStoreImpl->GetSptrRefCount()); + static_cast(singleKvStoreImpl.GetRefPtr())->IncreaseOpenCount(); + callback(std::move(singleKvStoreImpl)); + return Status::SUCCESS; + } + + if ((GetTotalKvStoreNum()) >= static_cast(Constant::MAX_OPEN_KVSTORES)) { + ZLOGE("limit %d KvStores can be opened.", Constant::MAX_OPEN_KVSTORES); + callback(nullptr); + return Status::ERROR; + } + + DistributedDB::KvStoreNbDelegate::Option dbOption; + auto status = InitNbDbOption(options, cipherKey, dbOption); + if (status != Status::SUCCESS) { + ZLOGE("InitNbDbOption failed."); + callback(nullptr); + return status; + } + + DistributedDB::KvStoreNbDelegate *storeDelegate = nullptr; + DistributedDB::DBStatus dbStatusTmp; + delegateManager->GetKvStore(storeId, dbOption, + [&](DistributedDB::DBStatus dbStatus, DistributedDB::KvStoreNbDelegate *kvStoreDelegate) { + storeDelegate = kvStoreDelegate; + dbStatusTmp = dbStatus; + }); + + if (storeDelegate == nullptr) { + ZLOGE("storeDelegate is nullptr."); + callback(nullptr); + return ConvertErrorStatus(dbStatusTmp, options.createIfMissing); + } + std::string kvStorePath = GetDbDir(options); + auto store = new (std::nothrow) DeviceKvStoreImpl({ + options, options.kvStoreType == KvStoreType::DEVICE_COLLABORATION, deviceAccountId_, bundleName_, storeId, + kvStorePath}, storeDelegate); + if (store == nullptr) { + ZLOGE("store is nullptr."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + return Status::ERROR; + } + auto result = singleStores_[type].emplace(storeId, store); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + delegateManager->CloseKvStore(storeDelegate); + delete store; + return Status::ERROR; + } + + sptr singleKvStoreImpl = result.first->second; + ZLOGI("after emplace refcount: %d autoSync: %d", + singleKvStoreImpl->GetSptrRefCount(), static_cast(options.autoSync)); + if (options.autoSync) { + bool autoSync = true; + DistributedDB::PragmaData data = static_cast(&autoSync); + auto pragmaStatus = storeDelegate->Pragma(DistributedDB::PragmaCmd::AUTO_SYNC, data); + if (pragmaStatus != DistributedDB::DBStatus::OK) { + ZLOGE("pragmaStatus: %d", static_cast(pragmaStatus)); + } + } + + callback(std::move(singleKvStoreImpl)); + DistributedDB::AutoLaunchOption launchOption = { + options.createIfMissing, options.encrypt, dbOption.cipher, dbOption.passwd, dbOption.schema, + dbOption.createDirByStoreIdOnly, kvStorePath, nullptr + }; + launchOption.secOption = ConvertSecurity(options.securityLevel); + AppAccessorParam accessorParam = {Constant::DEFAULT_GROUP_ID, trueAppId_, storeId, launchOption}; + KvStoreAppAccessor::GetInstance().EnableKvStoreAutoLaunch(accessorParam); + return Status::SUCCESS; +} + +Status KvStoreAppManager::CloseKvStore(const std::string &storeId) +{ + ZLOGI("CloseKvStore"); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + std::lock_guard lg(storeMutex_); + Status status = CloseKvStore(storeId, PATH_DE); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + + status = CloseKvStore(storeId, PATH_CE); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + + ZLOGW("store not open"); + return Status::STORE_NOT_OPEN; +} + +Status KvStoreAppManager::CloseAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(storeMutex_); + if (GetTotalKvStoreNum() == 0) { + return Status::STORE_NOT_OPEN; + } + + ZLOGI("close %zu KvStores.", GetTotalKvStoreNum()); + Status status = CloseAllKvStore(PATH_DE); + if (status == Status::DB_ERROR) { + return status; + } + status = CloseAllKvStore(PATH_CE); + if (status == Status::DB_ERROR) { + return status; + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteKvStore(const std::string &storeId) +{ + ZLOGI("%s", storeId.c_str()); + if (!flowCtrlManager_.IsTokenEnough()) { + ZLOGE("flow control denied"); + return Status::EXCEED_MAX_ACCESS_RATE; + } + + Status statusDE = DeleteKvStore(storeId, PATH_DE); + Status statusCE = DeleteKvStore(storeId, PATH_CE); + if (statusDE == Status::SUCCESS || statusCE == Status::SUCCESS) { + return Status::SUCCESS; + } + + ZLOGE("delegate close error."); + return Status::DB_ERROR; +} + +Status KvStoreAppManager::DeleteAllKvStore() +{ + ZLOGI("begin."); + std::lock_guard lg(storeMutex_); + if (GetTotalKvStoreNum() == 0) { + return Status::STORE_NOT_OPEN; + } + ZLOGI("delete %d KvStores.", int32_t(GetTotalKvStoreNum())); + + Status status = DeleteAllKvStore(PATH_DE); + if (status != Status::SUCCESS) { + ZLOGE("path de delegate delete error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + status = DeleteAllKvStore(PATH_CE); + if (status != Status::SUCCESS) { + ZLOGE("path ce delegate delete error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + return Status::SUCCESS; +} + +Status KvStoreAppManager::MigrateAllKvStore(const std::string &harmonyAccountId) +{ + ZLOGI("begin"); + if (PermissionValidator::IsAutoLaunchEnabled(bundleName_)) { + return Status::SUCCESS; + } + + std::lock_guard lg(storeMutex_); + // update userid in kvstore tuple map of permission adapter. + KvStoreTuple srcKvStoreTuple {userId_, bundleName_}; + KvStoreTuple dstKvStoreTuple {harmonyAccountId, bundleName_}; + PermissionValidator::UpdateKvStoreTupleMap(srcKvStoreTuple, dstKvStoreTuple); + userId_ = harmonyAccountId; + ZLOGI("path de migration begin."); + Status statusDE = MigrateAllKvStore(harmonyAccountId, PATH_DE); + ZLOGI("path ce migration begin."); + Status statusCE = MigrateAllKvStore(harmonyAccountId, PATH_CE); + return (statusCE != Status::SUCCESS) ? statusCE : statusDE; +} + +Status KvStoreAppManager::InitDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreDelegate::Option &dbOption) +{ + DistributedDB::CipherPassword password; + auto status = password.SetValue(cipherKey.data(), cipherKey.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd:%zu", cipherKey.size()); + return Status::DB_ERROR; + } + dbOption.createIfNecessary = options.createIfMissing; + dbOption.localOnly = false; + dbOption.isEncryptedDb = options.encrypt; + if (options.encrypt) { + dbOption.cipher = DistributedDB::CipherType::AES_256_GCM; + dbOption.passwd = password; + } + dbOption.createDirByStoreIdOnly = options.dataOwnership; + return Status::SUCCESS; +} + +Status KvStoreAppManager::InitNbDbOption(const Options &options, const std::vector &cipherKey, + DistributedDB::KvStoreNbDelegate::Option &dbOption) +{ + DistributedDB::CipherPassword password; + auto status = password.SetValue(cipherKey.data(), cipherKey.size()); + if (status != DistributedDB::CipherPassword::ErrorCode::OK) { + ZLOGE("Failed to set the passwd:%zu", cipherKey.size()); + return Status::DB_ERROR; + } + + dbOption.createIfNecessary = options.createIfMissing; + dbOption.isEncryptedDb = options.encrypt; + if (options.encrypt) { + dbOption.cipher = DistributedDB::CipherType::AES_256_GCM; + dbOption.passwd = password; + } + + if (options.kvStoreType == KvStoreType::SINGLE_VERSION) { + dbOption.conflictResolvePolicy = DistributedDB::LAST_WIN; + } else if (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION) { + dbOption.conflictResolvePolicy = DistributedDB::DEVICE_COLLABORATION; + } else { + ZLOGE("kvStoreType is invalid"); + return Status::INVALID_ARGUMENT; + } + + dbOption.schema = options.schema; + dbOption.createDirByStoreIdOnly = options.dataOwnership; + dbOption.secOption = ConvertSecurity(options.securityLevel); + return Status::SUCCESS; +} + +std::string KvStoreAppManager::GetDbDir(const Options &options) const +{ + return GetDataStoragePath(deviceAccountId_, bundleName_, ConvertPathType(bundleName_, options.securityLevel)); +} + +KvStoreAppManager::PathType KvStoreAppManager::ConvertPathType(const std::string &bundleName, int securityLevel) +{ + PathType type = PATH_CE; + if ((securityLevel == NO_LABEL && PermissionValidator::IsSystemService(bundleName)) || + securityLevel == S0 || + securityLevel == S1) { + type = PATH_DE; + } + return type; +} + +DistributedDB::KvStoreDelegateManager *KvStoreAppManager::GetDelegateManager(PathType type) +{ + std::lock_guard guard(delegateMutex_); + if (delegateManagers_[type] != nullptr) { + return delegateManagers_[type]; + } + + std::string directory = GetDataStoragePath(deviceAccountId_, bundleName_, type); + bool ret = ForceCreateDirectory(directory); + if (!ret) { + ZLOGE("create directory[%s] failed, errstr=[%d].", directory.c_str(), errno); + return nullptr; + } + // change mode for directories to 0755, and for files to 0600. + DirectoryUtils::ChangeModeDirOnly(directory, Constant::DEFAULT_MODE_DIR); + DirectoryUtils::ChangeModeFileOnly(directory, Constant::DEFAULT_MODE_FILE); + + trueAppId_ = KvStoreUtils::GetAppIdByBundleName(bundleName_); + if (trueAppId_.empty()) { + delegateManagers_[type] = nullptr; + ZLOGW("trueAppId_ empty(permission issues?)"); + return nullptr; + } + + userId_ = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(bundleName_); + ZLOGD("accountId: %s bundleName: %s", KvStoreUtils::ToBeAnonymous(userId_).c_str(), bundleName_.c_str()); + delegateManagers_[type] = new (std::nothrow) DistributedDB::KvStoreDelegateManager(trueAppId_, userId_); + if (delegateManagers_[type] == nullptr) { + ZLOGE("delegateManagers_[%d] is nullptr.", type); + return nullptr; + } + + DistributedDB::KvStoreConfig kvStoreConfig; + kvStoreConfig.dataDir = directory; + delegateManagers_[type]->SetKvStoreConfig(kvStoreConfig); + DistributedDB::KvStoreDelegateManager::SetProcessLabel(Constant::PROCESS_LABEL, "default"); + auto communicator = std::make_shared(); + auto result = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + ZLOGI("app set communicator result:%d.", static_cast(result)); + return delegateManagers_[type]; +} + +DistributedDB::KvStoreDelegateManager *KvStoreAppManager::SwitchDelegateManager(PathType type, + DistributedDB::KvStoreDelegateManager *delegateManager) +{ + std::lock_guard guard(delegateMutex_); + DistributedDB::KvStoreDelegateManager *oldDelegateManager = delegateManagers_[type]; + delegateManagers_[type] = delegateManager; + return oldDelegateManager; +} + +Status KvStoreAppManager::CloseKvStore(const std::string &storeId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + ZLOGD("find store and close delegate."); + InnerStatus status = it->second->Close(delegateManager); + if (status == InnerStatus::SUCCESS) { + stores_[type].erase(it); + return Status::SUCCESS; + } + if (status == InnerStatus::DECREASE_REFCOUNT) { + return Status::SUCCESS; + } + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + auto itSingle = singleStores_[type].find(storeId); + if (itSingle != singleStores_[type].end()) { + ZLOGD("find single store and close delegate."); + InnerStatus status = itSingle->second->Close(delegateManager); + if (status == InnerStatus::SUCCESS) { + singleStores_[type].erase(itSingle); + return Status::SUCCESS; + } + if (status == InnerStatus::DECREASE_REFCOUNT) { + return Status::SUCCESS; + } + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + return Status::STORE_NOT_OPEN; +} + +Status KvStoreAppManager::CloseAllKvStore(PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + for (auto it = stores_[type].begin(); it != stores_[type].end(); it = stores_[type].erase(it)) { + KvStoreImpl *currentStore = it->second.GetRefPtr(); + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + } + stores_[type].clear(); + + for (auto it = singleStores_[type].begin(); it != singleStores_[type].end(); it = singleStores_[type].erase(it)) { + SingleKvStoreImpl *currentStore = it->second.GetRefPtr(); + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate close error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + } + singleStores_[type].clear(); + return Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteKvStore(const std::string &storeId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + std::lock_guard lg(storeMutex_); + auto it = stores_[type].find(storeId); + if (it != stores_[type].end()) { + Status status = it->second->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + return Status::DB_ERROR; + } + stores_[type].erase(it); + } + + auto itSingle = singleStores_[type].find(storeId); + if (itSingle != singleStores_[type].end()) { + Status status = itSingle->second->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + return Status::DB_ERROR; + } + singleStores_[type].erase(itSingle); + } + + DistributedDB::DBStatus status = delegateManager->DeleteKvStore(storeId); + if (singleStores_[type].empty() && stores_[type].empty()) { + SwitchDelegateManager(type, nullptr); + delete delegateManager; + } + return (status != DistributedDB::DBStatus::OK) ? Status::DB_ERROR : Status::SUCCESS; +} + +Status KvStoreAppManager::DeleteAllKvStore(PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager[%d] is null.", type); + return Status::ILLEGAL_STATE; + } + + for (auto it = stores_[type].begin(); it != stores_[type].end(); it = stores_[type].erase(it)) { + std::string storeId = it->first; + KvStoreImpl *currentStore = it->second.GetRefPtr(); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate delete close failed error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + ZLOGI("delete kvstore, refcount %d.", it->second->GetSptrRefCount()); + DistributedDB::DBStatus dbStatus = delegateManager->DeleteKvStore(storeId); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate delete error: %d.", static_cast(dbStatus)); + return Status::DB_ERROR; + } + } + stores_[type].clear(); + + for (auto it = singleStores_[type].begin(); it != singleStores_[type].end(); it = singleStores_[type].erase(it)) { + std::string storeId = it->first; + SingleKvStoreImpl *currentStore = it->second.GetRefPtr(); + Status status = currentStore->ForceClose(delegateManager); + if (status != Status::SUCCESS) { + ZLOGE("delegate delete close failed error: %d.", static_cast(status)); + return Status::DB_ERROR; + } + + ZLOGI("close kvstore, refcount %d.", it->second->GetSptrRefCount()); + DistributedDB::DBStatus dbStatus = delegateManager->DeleteKvStore(storeId); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("delegate delete error: %d.", static_cast(dbStatus)); + return Status::DB_ERROR; + } + } + singleStores_[type].clear(); + SwitchDelegateManager(type, nullptr); + delete delegateManager; + return Status::SUCCESS; +} + +Status KvStoreAppManager::MigrateAllKvStore(const std::string &harmonyAccountId, PathType type) +{ + auto *delegateManager = GetDelegateManager(type); + if (delegateManager == nullptr) { + ZLOGE("delegateManager is nullptr."); + return Status::ILLEGAL_STATE; + } + + std::string dirPath = GetDataStoragePath(deviceAccountId_, bundleName_, type); + DistributedDB::KvStoreDelegateManager *newDelegateManager = nullptr; + Status status = Status::SUCCESS; + ZLOGI("KvStore migration begin."); + for (auto &it : stores_[type]) { + sptr impl = it.second; + if (impl->MigrateKvStore(harmonyAccountId, dirPath, delegateManager, newDelegateManager) != Status::SUCCESS) { + status = Status::MIGRATION_KVSTORE_FAILED; + ZLOGE("migrate kvstore for appId-%s failed.", bundleName_.c_str()); + // skip this failed, continue to migrate other kvstore. + } + } + + ZLOGI("SingleKvStore migration begin."); + for (auto &it : singleStores_[type]) { + sptr impl = it.second; + if (impl->MigrateKvStore(harmonyAccountId, dirPath, delegateManager, newDelegateManager) != Status::SUCCESS) { + status = Status::MIGRATION_KVSTORE_FAILED; + ZLOGE("migrate single kvstore for appId-%s failed.", bundleName_.c_str()); + // skip this failed, continue to migrate other kvstore. + } + } + + if (newDelegateManager != nullptr) { + delegateManager = SwitchDelegateManager(type, newDelegateManager); + delete delegateManager; + } + return status; +} + +size_t KvStoreAppManager::GetTotalKvStoreNum() const +{ + size_t total = stores_[PATH_DE].size(); + total += stores_[PATH_CE].size(); + total += singleStores_[PATH_DE].size(); + total += singleStores_[PATH_CE].size(); + return int(total); +}; + +std::string KvStoreAppManager::GetDataStoragePath(const std::string &deviceAccountId, const std::string &bundleName, + PathType type) +{ + std::string miscPath = (type == PATH_DE) ? Constant::ROOT_PATH_DE : Constant::ROOT_PATH_CE; + return Constant::Concatenate({ + miscPath, "/", Constant::SERVICE_NAME, "/", deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), + "/", bundleName, "/" + }); +} + +DistributedDB::SecurityOption KvStoreAppManager::ConvertSecurity(int securityLevel) +{ + if (securityLevel < SecurityLevel::NO_LABEL || securityLevel > SecurityLevel::S4) { + return {DistributedDB::NOT_SET, DistributedDB::ECE}; + } + switch (securityLevel) { + case SecurityLevel::S3: + return {DistributedDB::S3, DistributedDB::SECE}; + case SecurityLevel::S4: + return {DistributedDB::S4, DistributedDB::ECE}; + default: + return {securityLevel, DistributedDB::ECE}; + } +} + +void KvStoreAppManager::Dump(int fd) const +{ + const std::string prefix(8, ' '); + std::string dePath = GetDataStoragePath(deviceAccountId_, bundleName_, PATH_DE); + std::string cePath = GetDataStoragePath(deviceAccountId_, bundleName_, PATH_CE); + size_t singleStoreNum = singleStores_[PATH_DE].size() + singleStores_[PATH_CE].size(); + dprintf(fd, "%s----------------------------------------------------------\n", prefix.c_str()); + dprintf(fd, "%sAppID : %s\n", prefix.c_str(), trueAppId_.c_str()); + dprintf(fd, "%sBundleName : %s\n", prefix.c_str(), bundleName_.c_str()); + dprintf(fd, "%sAppDEDirectory: %s\n", prefix.c_str(), dePath.c_str()); + dprintf(fd, "%sAppCEDirectory: %s\n", prefix.c_str(), cePath.c_str()); + dprintf(fd, "%sStore count : %u\n", prefix.c_str(), static_cast(singleStoreNum)); + for (const auto &singleStoreMap : singleStores_) { + for (const auto &pair : singleStoreMap) { + pair.second->OnDump(fd); + } + } +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/test/unittest/kvstore_backup_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_backup_test.cpp new file mode 100755 index 000000000..5525ce944 --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_backup_test.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kvstore_impl.h" +#include "backup_handler.h" +#include "kv_scheduler.h" +#include "kvstore_data_service.h" +#include "kvstore_meta_manager.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "single_kvstore_impl.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; + +class KvStoreBackupTest : public testing::Test { +public: + static constexpr unsigned int DEFAULT_DIR_MODE = 0755; + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvStoreBackupTest::SetUpTestCase(void) +{} + +void KvStoreBackupTest::TearDownTestCase(void) +{} + +void KvStoreBackupTest::SetUp(void) +{ + const std::string backupDir = "/data/misc_ce/0/mdds/0/default/backup"; + + unlink(backupDir.c_str()); + mkdir(backupDir.c_str(), KvStoreBackupTest::DEFAULT_DIR_MODE); +} + +void KvStoreBackupTest::TearDown(void) +{} + +/** +* @tc.name: KvStoreBackupTest001 +* @tc.desc: set option backup +* @tc.type: FUNC +* @tc.require: SR000DR9J0 AR000DR9J1 +* @tc.author: guodaoxin +*/ +HWTEST_F(KvStoreBackupTest, KvStoreBackupTest001, TestSize.Level1) +{ + Options options = { .createIfMissing = true, .encrypt = false, .autoSync = true, .backup = true, + .kvStoreType = KvStoreType::SINGLE_VERSION, .dataOwnership = true }; + AppId appId = { "backup1" }; + StoreId storeId = { "store1" }; + KvStoreDataService kvDataService; + kvDataService.DeleteKvStore(appId, storeId); + sptr kvStorePtr; + Status status = kvDataService.GetSingleKvStore(options, appId, storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "KvStoreBackupTest001 set backup true failed"; + kvDataService.CloseKvStore(appId, storeId); +} + +/** +* @tc.name: KvStoreBackupTest002 +* @tc.desc: kvstore backup test for single db +* @tc.type: FUNC +* @tc.require: SR000DR9J0 AR000DR9JM +* @tc.author: guodaoxin +*/ +HWTEST_F(KvStoreBackupTest, KvStoreBackupTest002, TestSize.Level1) +{ + Options options = { .createIfMissing = true, .encrypt = false, .autoSync = true, .backup = true, + .kvStoreType = KvStoreType::SINGLE_VERSION, .dataOwnership = true }; + AppId appId = { "backup2" }; + StoreId storeId = { "store2" }; + + KvStoreDataService kvDataService; + kvDataService.DeleteKvStore(appId, storeId); + sptr kvStorePtr; + Status status = kvDataService.GetSingleKvStore(options, appId, storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore);}); + + EXPECT_EQ(status, Status::SUCCESS) << "KvStoreBackupTest002 set backup true failed"; + + Key key1("test1_key"); + Value value1("test1_value"); + kvStorePtr->Put(key1, value1); + Key key2("test2_key"); + Value value2("test2_value"); + kvStorePtr->Put(key2, value2); + + auto backupHandler = std::make_unique(); + auto trueAppId = KvStoreUtils::GetAppIdByBundleName(appId.appId); + MetaData metaData{0}; + metaData.kvStoreMetaData.deviceAccountId = "0"; + metaData.kvStoreMetaData.userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + metaData.kvStoreMetaData.appId = trueAppId; + metaData.kvStoreMetaData.storeId = storeId.storeId; + metaData.kvStoreMetaData.isBackup = false; + metaData.kvStoreType = KvStoreType::SINGLE_VERSION; + + backupHandler->SingleKvStoreBackup(metaData); + + kvStorePtr->Delete(key2); + Value value22; + kvStorePtr->Get(key2, value22); + + auto kptr = static_cast(kvStorePtr.GetRefPtr()); + kptr->Import(appId.appId); + kvStorePtr->Get(key2, value22); + + kvDataService.CloseKvStore(appId, storeId); +} + +/** +* @tc.name: KvStoreBackupTest003 +* @tc.desc: kvstore backup test for multi db +* @tc.type: FUNC +* @tc.require: AR000DR9JM AR000D08K5 +* @tc.author: guodaoxin +*/ +HWTEST_F(KvStoreBackupTest, KvStoreBackupTest003, TestSize.Level1) +{ + Options options = { .createIfMissing = true, .encrypt = false, .autoSync = true, .backup = true, + .kvStoreType = KvStoreType::SINGLE_VERSION, .dataOwnership = true }; + AppId appId = { "backup3" }; + StoreId storeId = { "store3" }; + + KvStoreDataService kvDataService; + kvDataService.DeleteKvStore(appId, storeId); + sptr kvStorePtr; + Status status = kvDataService.GetKvStore(options, appId, storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore);}); + + EXPECT_EQ(status, Status::SUCCESS) << "KvStoreBackupTest003 set backup true failed"; + + Key key1("test1_key"); + Value value1("test1_value"); + kvStorePtr->Put(key1, value1); + Key key2("test2_key"); + Value value2("test2_value"); + kvStorePtr->Put(key2, value2); + + auto backupHandler = std::make_unique(); + auto trueAppId = KvStoreUtils::GetAppIdByBundleName(appId.appId); + + MetaData metaData{0}; + metaData.kvStoreMetaData.deviceAccountId = "0"; + metaData.kvStoreMetaData.userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + metaData.kvStoreMetaData.appId = trueAppId; + metaData.kvStoreMetaData.storeId = storeId.storeId; + metaData.kvStoreMetaData.isBackup = false; + metaData.kvStoreType = KvStoreType::MULTI_VERSION; + + backupHandler->MultiKvStoreBackup(metaData); + + kvStorePtr->Delete(key2); + + auto kptr = static_cast(kvStorePtr.GetRefPtr()); + kptr->Import(appId.appId); + + sptr kvStoreSnapshotPtr; + kvStorePtr->GetKvStoreSnapshot(nullptr, + [&](Status status, sptr kvStoreSnapshot) { + kvStoreSnapshotPtr = std::move(kvStoreSnapshot); + }); + + EXPECT_NE(nullptr, kvStoreSnapshotPtr) << "KvStoreBackupTest003, kvStoreSnapshotPtr is nullptr"; + + Value value22; + kvStoreSnapshotPtr->Get(key2, value22); + + kvStorePtr->ReleaseKvStoreSnapshot(std::move(kvStoreSnapshotPtr)); + kvDataService.CloseKvStore(appId, storeId); +} + +/** +* @tc.name: KvStoreBackupTest004 +* @tc.desc: kvstore backup delete test +* @tc.type: FUNC +* @tc.require: SR000DR9J0 AR000DR9JN +* @tc.author: guodaoxin +*/ +HWTEST_F(KvStoreBackupTest, KvStoreBackupTest004, TestSize.Level1) +{ + Options options = { .createIfMissing = true, .encrypt = false, .autoSync = true, .backup = true, + .kvStoreType = KvStoreType::SINGLE_VERSION, .dataOwnership = true }; + AppId appId = { "backup4" }; + StoreId storeId = { "store4" }; + + KvStoreDataService kvDataService; + kvDataService.DeleteKvStore(appId, storeId); + sptr kvStorePtr; + Status status = kvDataService.GetSingleKvStore(options, appId, storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore);}); + + EXPECT_EQ(status, Status::SUCCESS) << "KvStoreBackupTest004 set backup true failed"; + + Key key1("test1_key"); + Value value1("test1_value"); + kvStorePtr->Put(key1, value1); + Key key2("test2_key"); + Value value2("test2_value"); + kvStorePtr->Put(key2, value2); + + auto backupHandler = std::make_unique(); + auto trueAppId = KvStoreUtils::GetAppIdByBundleName(appId.appId); + + MetaData metaData{0}; + metaData.kvStoreMetaData.deviceAccountId = "0"; + metaData.kvStoreMetaData.userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + metaData.kvStoreMetaData.appId = trueAppId; + metaData.kvStoreMetaData.storeId = storeId.storeId; + metaData.kvStoreMetaData.isBackup = false; + metaData.kvStoreType = KvStoreType::SINGLE_VERSION; + + backupHandler->SingleKvStoreBackup(metaData); + + auto backupFileName = BackupHandler::GetHashedBackupName(Constant::Concatenate({ AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), + "_", trueAppId, "_", storeId.storeId })); + auto pathType = KvStoreAppManager::ConvertPathType(appId.appId, metaData.kvStoreMetaData.securityLevel); + auto backFilePath = Constant::Concatenate({BackupHandler::GetBackupPath("0", pathType), "/", backupFileName}); + bool ret = BackupHandler::FileExists(backFilePath); + EXPECT_EQ(true, ret) << "KvStoreBackupTest004 backup file failed"; + + kvDataService.CloseKvStore(appId, storeId); + kvDataService.DeleteKvStore(appId, storeId); + + ret = BackupHandler::FileExists(backFilePath); + EXPECT_EQ(false, ret) << "KvStoreBackupTest004 delete backup file failed"; +} diff --git a/services/distributeddataservice/app/test/unittest/kvstore_data_service.cpp b/services/distributeddataservice/app/test/unittest/kvstore_data_service.cpp new file mode 100644 index 000000000..c5ee3beea --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_data_service.cpp @@ -0,0 +1,1328 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KvStoreDataService" + +#include "kvstore_data_service.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "auto_launch_export.h" +#include "communication_provider.h" +#include "constant.h" +#include "crypto_utils.h" +#include "dds_trace.h" +#include "device_change_listener_impl.h" +#include "device_kvstore_impl.h" +#include "iservice_registry.h" +#include "kvstore_account_observer.h" +#include "kvstore_app_accessor.h" +#include "kvstore_meta_manager.h" +#include "kvstore_utils.h" +#include "log_print.h" +#include "permission_validator.h" +#include "process_communicator_impl.h" +#include "reporter.h" +#include "system_ability_definition.h" +#include "uninstaller/uninstaller.h" + +namespace OHOS { +namespace DistributedKv { +using json = nlohmann::json; +using namespace std::chrono; + +REGISTER_SYSTEM_ABILITY_BY_ID(KvStoreDataService, DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID, true); + +constexpr size_t MAX_APP_ID_LENGTH = 256; + +KvStoreDataService::KvStoreDataService(bool runOnCreate) + : SystemAbility(runOnCreate), + accountMutex_(), + deviceAccountMap_(), + clientDeathObserverMutex_(), + clientDeathObserverMap_() +{ + ZLOGI("begin."); + Initialize(); +} + +KvStoreDataService::KvStoreDataService(int32_t systemAbilityId, bool runOnCreate) + : SystemAbility(systemAbilityId, runOnCreate), + accountMutex_(), + deviceAccountMap_(), + clientDeathObserverMutex_(), + clientDeathObserverMap_() +{ + ZLOGI("begin"); + Initialize(); +} + +KvStoreDataService::~KvStoreDataService() +{ + ZLOGI("begin."); + deviceAccountMap_.clear(); +} + +void KvStoreDataService::Initialize() +{ + ZLOGI("begin."); + KvStoreMetaManager::GetInstance().InitMetaParameter(); + std::thread th = std::thread([]() { + auto communicator = std::make_shared(); + auto ret = DistributedDB::KvStoreDelegateManager::SetProcessCommunicator(communicator); + ZLOGI("set communicator ret:%d.", static_cast(ret)); + if (KvStoreMetaManager::GetInstance().CheckRootKeyExist() == Status::SUCCESS) { + return; + } + constexpr int RETRY_MAX_TIMES = 100; + int retryCount = 0; + constexpr int RETRY_TIME_INTERVAL_MILLISECOND = 1 * 1000 * 1000; // retry after 1 second + while (retryCount < RETRY_MAX_TIMES) { + if (KvStoreMetaManager::GetInstance().GenerateRootKey() == Status::SUCCESS) { + ZLOGI("GenerateRootKey success."); + break; + } + retryCount++; + ZLOGE("GenerateRootKey failed."); + usleep(RETRY_TIME_INTERVAL_MILLISECOND); + } + }); + th.detach(); + + accountEventObserver_ = std::make_shared(*this); + AccountDelegate::GetInstance()->Subscribe(accountEventObserver_); +} + +bool KvStoreDataService::CheckBundleName(const std::string &bundleName) const +{ + if (bundleName.empty() || bundleName.size() > MAX_APP_ID_LENGTH || + bundleName.find(Constant::KEY_SEPARATOR) != std::string::npos) { + return false; + } + + auto iter = std::find_if_not(bundleName.begin(), bundleName.end(), + [](char c) { return (std::isprint(c) && c != '/'); }); + + return (iter == bundleName.end()); +} + +bool KvStoreDataService::CheckStoreId(const std::string &storeId) const +{ + if (storeId.empty() || storeId.size() > Constant::MAX_STORE_ID_LENGTH || + storeId.find(Constant::KEY_SEPARATOR) != std::string::npos) { + return false; + } + + auto iter = std::find_if_not(storeId.begin(), storeId.end(), + [](char c) { return (std::isdigit(c) || std::isalpha(c) || c == '_'); }); + + return (iter == storeId.end()); +} + +Status KvStoreDataService::GetKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + ZLOGI("begin."); + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + if (callback == nullptr) { + ZLOGW("callback is nullptr"); + return Status::ERROR; + } + if (appId.appId.empty() || storeId.storeId.empty()) { + ZLOGW("appid or storeid empty"); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KvStoreType kvStoreType = options.kvStoreType; + if (kvStoreType != KvStoreType::DEVICE_COLLABORATION && kvStoreType != KvStoreType::SINGLE_VERSION && + kvStoreType != KvStoreType::MULTI_VERSION) { + ZLOGE("invalid kvStore type."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGW("appId empty(permission issues?)"); + callback(nullptr); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + callback(nullptr); + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + if (!CheckOptions(options, metaKey)) { + callback(nullptr); + ZLOGE("encrypt type or kvStore type is not the same"); + return Status::INVALID_ARGUMENT; + } + std::vector secretKey; + std::unique_ptr, void (*)(std::vector *)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + + bool outdated = false; + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp, "KEY"); + auto secretKeyFile = KvStoreMetaManager::GetSecretKeyFile(deviceAccountId, bundleName, storeIdTmp); + Status alreadyCreated = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaSecretKey, CHECK_EXIST_LOCAL); + if (options.encrypt) { + ZLOGI("Getting secret key"); + if (alreadyCreated != Status::SUCCESS) { + ZLOGI("new secret key"); + CryptoUtils::GetRandomKey(32, secretKey); // 32 is key length + KvStoreMetaManager::GetInstance().WriteSecretKeyToMeta(metaSecretKey, secretKey); + KvStoreMetaManager::GetInstance().WriteSecretKeyToFile(secretKeyFile, secretKey); + } else { + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGW("get secret key from meta failed, try to recover"); + KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + } + if (secretKey.empty()) { + ZLOGW("recover failed"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + } else { + if (alreadyCreated == Status::SUCCESS || FileExists(secretKeyFile)) { + ZLOGW("try to get an encrypted store with false option encrypt parameter"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + auto result = deviceAccountMap_.emplace(std::piecewise_construct, + std::forward_as_tuple(deviceAccountId), std::forward_as_tuple(deviceAccountId)); + if (!result.second) { + ZLOGE("emplace failed."); + FaultMsg msg = {FaultType::RUNTIME_FAULT, "user", __FUNCTION__, Fault::RF_GET_DB}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + callback(nullptr); + return Status::ERROR; + } + it = result.first; + } + Status statusTmp = (it->second).GetKvStore( + options, bundleName, storeIdTmp, secretKey, + [&](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }); + + ZLOGD("get kvstore return status:%d, deviceAccountId:[%s], bundleName:[%s].", + statusTmp, KvStoreUtils::ToBeAnonymous(deviceAccountId).c_str(), bundleName.c_str()); + if (statusTmp == Status::SUCCESS) { + struct KvStoreMetaData metaData { + .appId = trueAppId, + .appType = "harmony", + .bundleName = bundleName, + .dataDir = "default", + .deviceAccountId = deviceAccountId, + .deviceId = DeviceKvStoreImpl::GetLocalDeviceId(), + .isAutoSync = options.autoSync, + .isBackup = options.backup, + .isEncrypt = options.encrypt, + .kvStoreType = options.kvStoreType, + .schema = options.schema, + .storeId = storeIdTmp, + .userId = Constant::DEFAULT_GROUP_ID, + .uid = IPCSkeleton::GetCallingUid(), + .version = KVSTORE_META_VERSION, + .securityLevel = SecurityLevel::NO_LABEL, + }; + std::string jsonStr = metaData.Marshal(); + std::vector jsonVec(jsonStr.begin(), jsonStr.end()); + + return KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, UPDATE, jsonVec); + } + Status getKvStoreStatus = statusTmp; + ZLOGW("getKvStore failed with status %d", static_cast(getKvStoreStatus)); + if (getKvStoreStatus == Status::CRYPT_ERROR && options.encrypt) { + if (alreadyCreated != Status::SUCCESS) { + // create encrypted store failed, remove secret key + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + return Status::ERROR; + } + // get existing encrypted store failed, retry with key stored in file + Status status = KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + if (status != Status::SUCCESS) { + callback(nullptr); + return Status::CRYPT_ERROR; + } + // here callback is called twice + statusTmp = (it->second).GetKvStore( + options, bundleName, storeIdTmp, secretKey, + [&](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }); + } + + // if kvstore damaged and no backup file, then return DB_ERROR + if (statusTmp != Status::SUCCESS && getKvStoreStatus == Status::CRYPT_ERROR) { + // if backup file not exist, dont need recover + if (!CheckBackupFileExist(deviceAccountId, bundleName, storeId.storeId, options.securityLevel)) { + return Status::CRYPT_ERROR; + } + // remove damaged database + if (DeleteKvStoreOnly(storeIdTmp, deviceAccountId, bundleName) != Status::SUCCESS) { + ZLOGE("DeleteKvStoreOnly failed."); + return Status::DB_ERROR; + } + // recover database + return RecoverMultiKvStore(options, deviceAccountId, bundleName, storeId, secretKey, callback); + } + + return statusTmp; +} + +Status KvStoreDataService::GetSingleKvStore(const Options &options, const AppId &appId, const StoreId &storeId, + std::function)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + if (callback == nullptr) { + ZLOGW("callback is nullptr"); + return Status::ERROR; + } + if (appId.appId.empty() || storeId.storeId.empty()) { + ZLOGW("appid or storeid empty"); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + KvStoreType kvStoreType = options.kvStoreType; + if (kvStoreType != KvStoreType::DEVICE_COLLABORATION && kvStoreType != KvStoreType::SINGLE_VERSION) { + ZLOGE("invalid kvStore type."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + callback(nullptr); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + callback(nullptr); + ZLOGW("appId empty(permission issues?)"); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + callback(nullptr); + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + if (!CheckOptions(options, metaKey)) { + callback(nullptr); + ZLOGE("encrypt type or kvStore type is not the same"); + return Status::INVALID_ARGUMENT; + } + std::vector secretKey; + std::unique_ptr, void (*)(std::vector *)> cleanGuard( + &secretKey, [](std::vector *ptr) { ptr->assign(ptr->size(), 0); }); + + bool outdated = false; + auto metaSecretKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp, + "SINGLE_KEY"); + auto secretKeyFile = KvStoreMetaManager::GetSecretSingleKeyFile(deviceAccountId, bundleName, storeIdTmp); + Status alreadyCreated = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaSecretKey, CHECK_EXIST_LOCAL); + if (options.encrypt) { + ZLOGI("Getting secret key"); + if (alreadyCreated != Status::SUCCESS) { + ZLOGI("new secret key"); + CryptoUtils::GetRandomKey(32, secretKey); // 32 is key length + KvStoreMetaManager::GetInstance().WriteSecretKeyToMeta(metaSecretKey, secretKey); + KvStoreMetaManager::GetInstance().WriteSecretKeyToFile(secretKeyFile, secretKey); + } else { + KvStoreMetaManager::GetInstance().GetSecretKeyFromMeta(metaSecretKey, secretKey, outdated); + if (secretKey.empty()) { + ZLOGW("get secret key from meta failed, try to recover"); + KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + } + if (secretKey.empty()) { + ZLOGW("recover failed"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + } else { + if (alreadyCreated == Status::SUCCESS || FileExists(secretKeyFile)) { + ZLOGW("try to get an encrypted store with false option encrypt parameter"); + callback(nullptr); + return Status::CRYPT_ERROR; + } + } + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + auto result = deviceAccountMap_.emplace(std::piecewise_construct, + std::forward_as_tuple(deviceAccountId), std::forward_as_tuple(deviceAccountId)); + if (!result.second) { + ZLOGE("emplace failed."); + callback(nullptr); + return Status::ERROR; + } + it = result.first; + } + auto newCallback = [&callback, outdated, deviceAccountId, bundleName, storeIdTmp](sptr store) { + if (outdated) { + KvStoreMetaManager::GetInstance().ReKey(deviceAccountId, bundleName, storeIdTmp, store); + } + callback(store); + }; + Status statusTmp = (it->second).GetSingleKvStore(options, bundleName, storeIdTmp, secretKey, newCallback); + if (statusTmp == Status::SUCCESS) { + KvStoreMetaData metaData { + .appId = trueAppId, + .appType = "harmony", + .bundleName = bundleName, + .dataDir = (it->second).GetDbDir(bundleName, options), + .deviceAccountId = deviceAccountId, + .deviceId = DeviceKvStoreImpl::GetLocalDeviceId(), + .isAutoSync = options.autoSync, + .isBackup = options.backup, + .isEncrypt = options.encrypt, + .kvStoreType = options.kvStoreType, + .schema = options.schema, + .storeId = storeIdTmp, + .userId = Constant::DEFAULT_GROUP_ID, + .uid = IPCSkeleton::GetCallingUid(), + .version = KVSTORE_META_VERSION, + .securityLevel = SecurityLevel::NO_LABEL, + }; + std::string jsonStr = metaData.Marshal(); + std::vector jsonVec(jsonStr.begin(), jsonStr.end()); + + return KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, UPDATE, jsonVec); + } + ZLOGW("getKvStore failed with status %d", static_cast(statusTmp)); + Status getKvStoreStatus = statusTmp; + if (getKvStoreStatus == Status::CRYPT_ERROR && options.encrypt) { + if (alreadyCreated != Status::SUCCESS) { + // create encrypted store failed, remove secret key + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + return Status::ERROR; + } + // get existing encrypted store failed, retry with key stored in file + Status status = KvStoreMetaManager::GetInstance().RecoverSecretKeyFromFile( + secretKeyFile, metaSecretKey, secretKey, outdated); + if (status != Status::SUCCESS) { + callback(nullptr); + return Status::CRYPT_ERROR; + } + // here callback is called twice + statusTmp = (it->second).GetSingleKvStore(options, bundleName, storeIdTmp, secretKey, newCallback); + } + + // if kvstore damaged and no backup file, then return DB_ERROR + if (statusTmp != Status::SUCCESS && getKvStoreStatus == Status::CRYPT_ERROR) { + // if backup file not exist, dont need recover + if (!CheckBackupFileExist(deviceAccountId, bundleName, storeId.storeId, options.securityLevel)) { + return Status::CRYPT_ERROR; + } + // remove damaged database + if (DeleteKvStoreOnly(storeIdTmp, deviceAccountId, bundleName) != Status::SUCCESS) { + ZLOGE("DeleteKvStoreOnly failed."); + return Status::DB_ERROR; + } + // recover database + return RecoverSingleKvStore(options, deviceAccountId, bundleName, storeId, secretKey, callback); + } + return statusTmp; +} + +bool KvStoreDataService::CheckOptions(const Options &options, const std::vector &metaKey) const +{ + ZLOGI("begin."); + KvStoreMetaData metaData; + metaData.version = 0; + Status statusTmp = KvStoreMetaManager::GetInstance().GetKvStoreMeta(metaKey, metaData); + if (statusTmp == Status::KEY_NOT_FOUND) { + ZLOGI("get metaKey not found."); + return true; + } + if (statusTmp != Status::SUCCESS) { + ZLOGE("get metaKey failed."); + return false; + } + ZLOGI("metaData encrypt is %d, kvStore type is %d, options encrypt is %d, kvStore type is %d", + static_cast(metaData.isEncrypt), static_cast(metaData.kvStoreType), + static_cast(options.encrypt), static_cast(options.kvStoreType)); + if (options.encrypt != metaData.isEncrypt) { + ZLOGE("checkOptions encrypt type is not the same."); + return false; + } + + if (options.kvStoreType != metaData.kvStoreType && metaData.version != 0) { + ZLOGE("checkOptions kvStoreType is not the same."); + return false; + } + ZLOGI("end."); + return true; +} + +bool KvStoreDataService::CheckBackupFileExist(const std::string &deviceAccountId, const std::string &bundleName, + const std::string &storeId, int securityLevel) +{ + auto pathType = KvStoreAppManager::ConvertPathType(bundleName, securityLevel); + auto backupFileName = Constant::Concatenate({ Constant::DEFAULT_GROUP_ID, "_", bundleName, "_", storeId }); + auto backFilePath = Constant::Concatenate({ BackupHandler::GetBackupPath(deviceAccountId, pathType), + "/", BackupHandler::GetHashedBackupName(backupFileName) }); + if (!BackupHandler::FileExists(backFilePath)) { + ZLOGE("BackupHandler file is not exist."); + return false; + } + return true; +} + +Status KvStoreDataService::RecoverSingleKvStore(const Options &options, + const std::string &deviceAccountId, + const std::string &bundleName, + const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback) +{ + // restore database + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + Options optionsTmp = options; + optionsTmp.createIfMissing = true; + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + ZLOGD("deviceAccountId not found"); + return Status::INVALID_ARGUMENT; + } + + sptr kvStorePtr; + Status statusTmp = (it->second).GetSingleKvStore( + optionsTmp, bundleName, storeIdTmp, secretKey, + [&kvStorePtr](sptr store) { kvStorePtr = store; }); + // restore database failed + if (statusTmp != Status::SUCCESS || kvStorePtr == nullptr) { + ZLOGE("RecoverSingleKvStore reget GetSingleKvStore failed."); + return Status::DB_ERROR; + } + // recover database from backup file + auto kvStorePtrTmp = static_cast(kvStorePtr.GetRefPtr()); + bool importRet = kvStorePtrTmp->Import(bundleName); + callback(kvStorePtr); + if (!importRet) { + ZLOGE("RecoverSingleKvStore Import failed."); + return Status::RECOVER_FAILED; + } + ZLOGD("RecoverSingleKvStore Import success."); + return Status::RECOVER_SUCCESS; +} + +Status KvStoreDataService::RecoverMultiKvStore(const Options &options, + const std::string &deviceAccountId, + const std::string &bundleName, + const StoreId &storeId, + const std::vector &secretKey, + std::function)> callback) +{ + // restore database + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + Options optionsTmp = options; + optionsTmp.createIfMissing = true; + + auto it = deviceAccountMap_.find(deviceAccountId); + if (it == deviceAccountMap_.end()) { + ZLOGD("deviceAccountId not found"); + return Status::INVALID_ARGUMENT; + } + + sptr kvStorePtr; + Status statusTmp = (it->second).GetKvStore( + optionsTmp, bundleName, storeIdTmp, secretKey, + [&kvStorePtr](sptr store) { + kvStorePtr = store; + }); + // restore database failed + if (statusTmp != Status::SUCCESS || kvStorePtr == nullptr) { + ZLOGE("RecoverMultiKvStore reget GetSingleKvStore failed."); + return Status::DB_ERROR; + } + + // recover database from backup file + auto kvStorePtrTmp = static_cast(kvStorePtr.GetRefPtr()); + if (!kvStorePtrTmp->Import(bundleName)) { + ZLOGE("RecoverMultiKvStore Import failed."); + return Status::RECOVER_FAILED; + } + ZLOGD("RecoverMultiKvStore Import success."); + callback(kvStorePtr); + return Status::RECOVER_SUCCESS; +} + +void KvStoreDataService::GetAllKvStoreId( + const AppId &appId, std::function &)> callback) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("GetAllKvStoreId begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::vector storeIdList; + if (bundleName.empty() || bundleName.size() > MAX_APP_ID_LENGTH) { + ZLOGE("invalid appId."); + callback(Status::INVALID_ARGUMENT, storeIdList); + return; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + callback(Status::PERMISSION_DENIED, storeIdList); + return; + } + + auto &metaKvStoreDelegate = KvStoreMetaManager::GetInstance().GetMetaKvStore(); + if (metaKvStoreDelegate == nullptr) { + ZLOGE("metaKvStoreDelegate is null"); + callback(Status::DB_ERROR, storeIdList); + return; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return; + } + std::vector dbEntries; + DistributedDB::DBStatus dbStatus; + DistributedDB::Key dbKey = KvStoreMetaRow::GetKeyFor( + DeviceKvStoreImpl::GetLocalDeviceId() + Constant::KEY_SEPARATOR + + deviceAccountId + Constant::KEY_SEPARATOR + + "default" + Constant::KEY_SEPARATOR + + bundleName + Constant::KEY_SEPARATOR); + { + DdsTrace trace(std::string(LOG_TAG "Delegate::") + std::string(__FUNCTION__)); + dbStatus = metaKvStoreDelegate->GetEntries(dbKey, dbEntries); + } + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("GetEntries delegate return error: %d.", static_cast(dbStatus)); + if (dbEntries.empty()) { + callback(Status::SUCCESS, storeIdList); + } else { + callback(Status::DB_ERROR, storeIdList); + } + return; + } + + for (const auto &entry : dbEntries) { + std::string keyStr = std::string(entry.key.begin(), entry.key.end()); + size_t pos = keyStr.find_last_of(Constant::KEY_SEPARATOR); + if (pos == std::string::npos) { + continue; + } + StoreId storeId; + storeId.storeId = keyStr.substr(pos + 1); + storeIdList.push_back(storeId); + } + callback(Status::SUCCESS, storeIdList); +} + +Status KvStoreDataService::CloseKvStore(const AppId &appId, const StoreId &storeId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + Status status = (it->second).CloseKvStore(bundleName, storeIdTmp); + if (status != Status::STORE_NOT_OPEN) { + return status; + } + } + FaultMsg msg = {FaultType::RUNTIME_FAULT, "user", __FUNCTION__, Fault::RF_CLOSE_DB}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + ZLOGE("return STORE_NOT_OPEN."); + return Status::STORE_NOT_OPEN; +} + +/* close all opened kvstore */ +Status KvStoreDataService::CloseAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGD("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + return (it->second).CloseAllKvStore(bundleName); + } + ZLOGE("store not open."); + return Status::STORE_NOT_OPEN; +} + +Status KvStoreDataService::DeleteKvStore(const AppId &appId, const StoreId &storeId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + std::string bundleName = appId.appId; + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + ZLOGE("get appId failed."); + return Status::PERMISSION_DENIED; + } + // delete the backup file + auto backupFileName = Constant::Concatenate({ + AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(), "_", bundleName, "_", storeId.storeId + }); + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + + auto backFilePath = Constant::Concatenate({ + BackupHandler::GetBackupPath(deviceAccountId, KvStoreAppManager::PATH_DE), "/", + BackupHandler::GetHashedBackupName(backupFileName) + }); + if (!BackupHandler::RemoveFile(backFilePath)) { + ZLOGE("DeleteKvStore RemoveFile backFilePath failed."); + } + backFilePath = Constant::Concatenate({ + BackupHandler::GetBackupPath(deviceAccountId, KvStoreAppManager::PATH_CE), "/", + BackupHandler::GetHashedBackupName(backupFileName) + }); + if (!BackupHandler::RemoveFile(backFilePath)) { + ZLOGE("DeleteKvStore RemoveFile backFilePath failed."); + } + return DeleteKvStore(appId, storeId, bundleName); +} + +/* delete all kv store */ +Status KvStoreDataService::DeleteAllKvStore(const AppId &appId) +{ + DdsTrace trace(std::string(LOG_TAG "::") + std::string(__FUNCTION__)); + + ZLOGI("%s", appId.appId.c_str()); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + + if (KvStoreUtils::GetAppIdByBundleName(bundleName).empty()) { + ZLOGE("invalid appId."); + return Status::PERMISSION_DENIED; + } + + Status statusTmp; + std::vector existStoreIds; + GetAllKvStoreId(appId, [&statusTmp, &existStoreIds](Status status, std::vector &storeIds) { + statusTmp = status; + existStoreIds = std::move(storeIds); + }); + + if (statusTmp != Status::SUCCESS) { + ZLOGE("%s, error: %d ", bundleName.c_str(), static_cast(statusTmp)); + return statusTmp; + } + + for (const auto &storeId : existStoreIds) { + statusTmp = DeleteKvStore(appId, storeId); + if (statusTmp != Status::SUCCESS) { + ZLOGE("%s, error: %d ", bundleName.c_str(), static_cast(statusTmp)); + return statusTmp; + } + } + + return statusTmp; +} + +/* RegisterClientDeathObserver */ +Status KvStoreDataService::RegisterClientDeathObserver(const AppId &appId, sptr observer) +{ + ZLOGD("begin."); + KVSTORE_ACCOUNT_EVENT_PROCESSING_CHECKER(Status::SYSTEM_ACCOUNT_EVENT_PROCESSING); + std::string bundleName = Constant::TrimCopy(appId.appId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(bundleName); + if (trueAppId.empty()) { + return Status::PERMISSION_DENIED; + } + + std::lock_guard lg(clientDeathObserverMutex_); + auto it = clientDeathObserverMap_.emplace(std::piecewise_construct, std::forward_as_tuple(bundleName), + std::forward_as_tuple(appId, *this, std::move(observer))); + ZLOGI("map size: %zu.", clientDeathObserverMap_.size()); + if (!it.second) { + ZLOGI("insert failed"); + return Status::ERROR; + } + ZLOGI("insert success"); + + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(); + KvStoreTuple kvStoreTuple {userId, trueAppId}; + AppThreadInfo appThreadInfo {IPCSkeleton::GetCallingPid(), IPCSkeleton::GetCallingUid()}; + PermissionValidator::RegisterPermissionChanged(kvStoreTuple, appThreadInfo); + return Status::SUCCESS; +} + +Status KvStoreDataService::AppExit(const AppId &appId) +{ + ZLOGI("AppExit"); + // memory of parameter appId locates in a member of clientDeathObserverMap_ and will be freed after + // clientDeathObserverMap_ erase, so we have to take a copy if we want to use this parameter after erase operation. + AppId appIdTmp = appId; + { + std::lock_guard lg(clientDeathObserverMutex_); + clientDeathObserverMap_.erase(appIdTmp.appId); + ZLOGI("map size: %zu.", clientDeathObserverMap_.size()); + } + + std::string trueAppId = KvStoreUtils::GetAppIdByBundleName(appIdTmp.appId); + if (trueAppId.empty()) { + ZLOGE("get appid for KvStore failed because of permission denied."); + return Status::PERMISSION_DENIED; + } + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId(appIdTmp.appId); + KvStoreTuple kvStoreTuple {userId, trueAppId}; + PermissionValidator::UnregisterPermissionChanged(kvStoreTuple); + + CloseAllKvStore(appIdTmp); + return Status::SUCCESS; +} + +void KvStoreDataService::OnDump() +{ + ZLOGD("begin."); +} + +int KvStoreDataService::Dump(int fd, const std::vector &args) +{ + int uid = static_cast(IPCSkeleton::GetCallingUid()); + const int maxUid = 10000; + if (uid > maxUid) { + return 0; + } + dprintf(fd, "------------------------------------------------------------------\n"); + dprintf(fd, "DeviceAccount count : %u\n", static_cast(deviceAccountMap_.size())); + for (const auto &pair : deviceAccountMap_) { + dprintf(fd, "DeviceAccountID : %s\n", pair.first.c_str()); + pair.second.Dump(fd); + } + return 0; +} + +void KvStoreDataService::OnStart() +{ + ZLOGI("distributeddata service onStart"); + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr != nullptr) { + ZLOGI("samgr exist."); + auto remote = samgr->CheckSystemAbility(DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID); + auto kvDataServiceProxy = iface_cast(remote); + if (kvDataServiceProxy != nullptr) { + ZLOGI("service has been registered."); + return; + } + } + + // register this to ServiceManager. + bool ret = SystemAbility::Publish(this); + if (!ret) { + FaultMsg msg = {FaultType::SERVICE_FAULT, "service", __FUNCTION__, Fault::SF_SERVICE_PUBLISH}; + Reporter::GetInstance()->ServiceFault()->Report(msg); + } + + Uninstaller::GetInstance().Init(this); + + // Initialize meta db delegate manager. + KvStoreMetaManager::GetInstance().InitMetaListener([this](const KvStoreMetaData &metaData) { + if (!metaData.isDirty) { + return; + } + + CloseKvStore({metaData.bundleName}, {metaData.storeId}); + DeleteKvStore({metaData.bundleName}, {metaData.storeId}); + }); + + // subscribe account event listener to EventNotificationMgr + AccountDelegate::GetInstance()->SubscribeAccountEvent(); + auto permissionCheckCallback = + [this](const std::string &userId, const std::string &appId, const std::string + &storeId, const std::string &deviceId, uint8_t flag) -> bool { + // temp add permission whilelist for ddmp; this should be config in ddmp manifest. + ZLOGD("checking sync permission start appid:%s, stid:%s.", appId.c_str(), storeId.c_str()); + return CheckPermissions(userId, appId, storeId, deviceId, flag); + }; + auto dbStatus = DistributedDB::KvStoreDelegateManager::SetPermissionCheckCallback(permissionCheckCallback); + if (dbStatus != DistributedDB::DBStatus::OK) { + ZLOGE("SetPermissionCheck callback failed."); + } + + ZLOGI("autoLaunchRequestCallback start"); + auto autoLaunchRequestCallback = + [this](const std::string &identifier, DistributedDB::AutoLaunchParam ¶m) -> bool { + ResolveAutoLaunchParamByIdentifier(identifier, param); + return true; + }; + DistributedDB::KvStoreDelegateManager::SetAutoLaunchRequestCallback(autoLaunchRequestCallback); + + backup_ = std::make_unique(this); + std::string backupPath = BackupHandler::GetBackupPath(AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, + KvStoreAppManager::PATH_CE); + ZLOGI("backupPath is : %s ", backupPath.c_str()); + if (!ForceCreateDirectory(backupPath)) { + ZLOGE("backup create directory failed."); + } + backup_->BackSchedule(); + std::thread th = std::thread([]() { + sleep(TEN_SEC); + KvStoreAppAccessor::GetInstance().EnableKvStoreAutoLaunch(); + }); + th.detach(); + ZLOGI("Publish ret: %d", static_cast(ret)); +} + +void KvStoreDataService::ResolveAutoLaunchParamByIdentifier(const std::string &identifier, + DistributedDB::AutoLaunchParam ¶m) +{ + ZLOGI("start"); + std::map entries; + if (KvStoreMetaManager::GetInstance().GetFullMetaData(entries)) { + for (const auto &entry : entries) { + const std::string userId = AccountDelegate::GetInstance()->GetCurrentHarmonyAccountId( + entry.second.kvStoreMetaData.bundleName); + const std::string &curIdentifier = DistributedDB::KvStoreDelegateManager::GetKvStoreIdentifier(userId, + entry.second.kvStoreMetaData.appId, entry.second.kvStoreMetaData.storeId); + if (identifier == curIdentifier) { + ZLOGI("identifier find"); + DistributedDB::AutoLaunchOption option; + option.createIfNecessary = false; + option.isEncryptedDb = entry.second.kvStoreMetaData.isEncrypt; + DistributedDB::CipherPassword password; + const std::vector &secretKey = entry.second.secretKeyMetaData.secretKey; + if (password.SetValue(secretKey.data(), secretKey.size()) != DistributedDB::CipherPassword::OK) { + ZLOGE("Get secret key failed."); + } + option.passwd = password; + option.schema = entry.second.kvStoreMetaData.schema; + option.createDirByStoreIdOnly = true; + option.dataDir = entry.second.kvStoreMetaData.dataDir; + option.secOption = KvStoreAppManager::ConvertSecurity(entry.second.kvStoreMetaData.securityLevel); + param.userId = userId; + param.appId = entry.second.kvStoreMetaData.appId; + param.storeId = entry.second.kvStoreMetaData.storeId; + param.option = option; + } + } + } +} + +bool KvStoreDataService::CheckPermissions(const std::string &userId, const std::string &appId, + const std::string &storeId, const std::string &deviceId, uint8_t flag) const +{ + auto &instance = KvStoreMetaManager::GetInstance(); + KvStoreMetaData metaData; + auto localDevId = DeviceKvStoreImpl::GetLocalDeviceId(); + auto qstatus = instance.QueryKvStoreMetaDataByDeviceIdAndAppId(localDevId, appId, metaData); + if (qstatus != Status::SUCCESS) { + qstatus = instance.QueryKvStoreMetaDataByDeviceIdAndAppId("", appId, metaData); // local device id maybe null + if (qstatus != Status::SUCCESS) { + ZLOGW("query appId failed."); + return false; + } + } + if (metaData.appType.compare("default") == 0) { + ZLOGD("default, dont check sync permission."); + return true; + } + + Status status = instance.CheckSyncPermission(userId, appId, storeId, flag, deviceId); + if (status != Status::SUCCESS) { + ZLOGW("PermissionCheck failed."); + return false; + } + + if (metaData.appType.compare("harmony") != 0) { + ZLOGD("it's A app, dont check sync permission."); + return true; + } + + if (PermissionValidator::IsAutoLaunchEnabled(appId)) { + return true; + } + bool ret = PermissionValidator::CheckSyncPermission(userId, appId, metaData.uid); + ZLOGD("checking sync permission ret:%d.", ret); + return ret; +} + +void KvStoreDataService::OnStop() +{ + ZLOGI("begin."); + if (backup_ != nullptr) { + backup_.reset(); + backup_ = nullptr; + } +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreClientDeathObserverImpl( + const AppId &appId, KvStoreDataService &service, sptr observer) + : appId_(appId), dataService_(service), observerProxy_(std::move(observer)), + deathRecipient_(new KvStoreDeathRecipient(*this)) +{ + ZLOGI("KvStoreClientDeathObserverImpl"); + + if (observerProxy_ != nullptr) { + ZLOGI("add death recipient"); + observerProxy_->AddDeathRecipient(deathRecipient_); + } else { + ZLOGW("observerProxy_ is nullptr"); + } +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::~KvStoreClientDeathObserverImpl() +{ + ZLOGI("~KvStoreClientDeathObserverImpl"); + if (deathRecipient_ != nullptr && observerProxy_ != nullptr) { + ZLOGI("remove death recipient"); + observerProxy_->RemoveDeathRecipient(deathRecipient_); + } +} + +void KvStoreDataService::KvStoreClientDeathObserverImpl::NotifyClientDie() +{ + ZLOGI("appId: %s", appId_.appId.c_str()); + dataService_.AppExit(appId_); +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::KvStoreDeathRecipient( + KvStoreClientDeathObserverImpl &kvStoreClientDeathObserverImpl) + : kvStoreClientDeathObserverImpl_(kvStoreClientDeathObserverImpl) +{ + ZLOGI("KvStore Client Death Observer"); +} + +KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::~KvStoreDeathRecipient() +{ + ZLOGI("KvStore Client Death Observer"); +} + +void KvStoreDataService::KvStoreClientDeathObserverImpl::KvStoreDeathRecipient::OnRemoteDied( + const wptr &remote) +{ + ZLOGI("begin"); + kvStoreClientDeathObserverImpl_.NotifyClientDie(); +} + +Status KvStoreDataService::DeleteKvStore(const AppId &appId, const StoreId &storeId, const std::string &trueAppId) +{ + ZLOGI("begin."); + std::string bundleName = Constant::TrimCopy(appId.appId); + std::string storeIdTmp = Constant::TrimCopy(storeId.storeId); + if (!CheckBundleName(bundleName)) { + ZLOGE("invalid bundleName."); + return Status::INVALID_ARGUMENT; + } + if (!CheckStoreId(storeIdTmp)) { + ZLOGE("invalid storeIdTmp."); + return Status::INVALID_ARGUMENT; + } + + const int32_t uid = IPCSkeleton::GetCallingUid(); + const std::string deviceAccountId = AccountDelegate::GetInstance()->GetDeviceAccountIdByUID(uid); + if (deviceAccountId != AccountDelegate::MAIN_DEVICE_ACCOUNT_ID) { + ZLOGE("not support sub account"); + return Status::NOT_SUPPORT; + } + std::lock_guard lg(accountMutex_); + Status status; + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + status = (it->second).DeleteKvStore(trueAppId, storeIdTmp); + } else { + KvStoreUserManager kvStoreUserManager(deviceAccountId); + status = kvStoreUserManager.DeleteKvStore(trueAppId, storeIdTmp); + } + + if (status == Status::SUCCESS) { + auto metaKey = KvStoreMetaManager::GetMetaKey(deviceAccountId, "default", bundleName, storeIdTmp); + status = KvStoreMetaManager::GetInstance().CheckUpdateServiceMeta(metaKey, DELETE); + if (status != Status::SUCCESS) { + ZLOGW("Remove Kvstore Metakey failed."); + } + KvStoreMetaManager::GetInstance().RemoveSecretKey(deviceAccountId, bundleName, storeIdTmp); + KvStoreMetaManager::GetInstance().DeleteStrategyMeta(bundleName, storeIdTmp); + } + return status; +} + + +Status KvStoreDataService::DeleteKvStoreOnly(const std::string &storeIdTmp, const std::string &deviceAccountId, + const std::string &bundleName) +{ + ZLOGI("DeleteKvStoreOnly begin."); + auto it = deviceAccountMap_.find(deviceAccountId); + if (it != deviceAccountMap_.end()) { + return (it->second).DeleteKvStore(bundleName, storeIdTmp); + } + KvStoreUserManager kvStoreUserManager(deviceAccountId); + return kvStoreUserManager.DeleteKvStore(bundleName, storeIdTmp); +} + +void KvStoreDataService::AccountEventChanged(const AccountEventInfo &eventInfo) +{ + ZLOGI("account event %d changed process, begin.", eventInfo.status); + std::lock_guard lg(accountMutex_); + switch (eventInfo.status) { + case AccountStatus::HARMONY_ACCOUNT_LOGIN: + case AccountStatus::HARMONY_ACCOUNT_LOGOUT: { + g_kvStoreAccountEventStatus = 1; + // migrate all kvstore belong to this device account + for (auto &it : deviceAccountMap_) { + (it.second).MigrateAllKvStore(eventInfo.harmonyAccountId); + } + g_kvStoreAccountEventStatus = 0; + break; + } + case AccountStatus::DEVICE_ACCOUNT_DELETE: { + g_kvStoreAccountEventStatus = 1; + // delete all kvstore belong to this device account + for (auto &it : deviceAccountMap_) { + (it.second).DeleteAllKvStore(); + } + auto it = deviceAccountMap_.find(eventInfo.deviceAccountId); + if (it != deviceAccountMap_.end()) { + deviceAccountMap_.erase(eventInfo.deviceAccountId); + } + std::string deviceAccountKvStoreDataDir = + Constant::Concatenate({Constant::ROOT_PATH_DE, "/", Constant::SERVICE_NAME, + "/", eventInfo.deviceAccountId}); + ForceRemoveDirectory(deviceAccountKvStoreDataDir); + deviceAccountKvStoreDataDir = + Constant::Concatenate({Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, + "/", eventInfo.deviceAccountId}); + ForceRemoveDirectory(deviceAccountKvStoreDataDir); + g_kvStoreAccountEventStatus = 0; + break; + } + default: { + break; + } + } + ZLOGI("account event %d changed process, end.", eventInfo.status); +} + +Status KvStoreDataService::GetLocalDevice(DeviceInfo &device) +{ + auto tmpDevice = KvStoreUtils::GetProviderInstance().GetLocalBasicInfo(); + device = {tmpDevice.deviceId, tmpDevice.deviceName, tmpDevice.deviceType}; + return Status::SUCCESS; +} + +Status KvStoreDataService::GetDeviceList(std::vector &deviceInfoList, DeviceFilterStrategy strategy) +{ + auto devices = KvStoreUtils::GetProviderInstance().GetRemoteNodesBasicInfo(); + for(auto const &device : devices) { + deviceInfoList.push_back({device.deviceId, device.deviceName, device.deviceType}); + } + ZLOGD("strategy is %d.", strategy); + return Status::SUCCESS; +} + +Status KvStoreDataService::StartWatchDeviceChange(sptr observer, + DeviceFilterStrategy strategy) +{ + if (observer == nullptr) { + ZLOGD("observer is null"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(deviceListenerMutex_); + if (deviceListener_ == nullptr) { + deviceListener_ = std::make_shared(deviceListeners_); + KvStoreUtils::GetProviderInstance().StartWatchDeviceChange(deviceListener_.get(), {"serviceWatcher"}); + } + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + deviceListeners_.insert({objectPtr, observer}); + ZLOGD("strategy is %d.", strategy); + return Status::SUCCESS; +} + +Status KvStoreDataService::StopWatchDeviceChange(sptr observer) +{ + if (observer == nullptr) { + ZLOGD("observer is null"); + return Status::INVALID_ARGUMENT; + } + std::lock_guard lck(deviceListenerMutex_); + IRemoteObject *objectPtr = observer->AsObject().GetRefPtr(); + auto it = deviceListeners_.find(objectPtr); + if (it == deviceListeners_.end()) { + return Status::ILLEGAL_STATE; + } + deviceListeners_.erase(it->first); + return Status::SUCCESS; +} + +bool DbMetaCallbackDelegateMgr::GetKvStoreDiskSize(const std::string &storeId, uint64_t &size) +{ + if (IsDestruct()) { + return false; + } + DistributedDB::DBStatus ret = delegate_->GetKvStoreDiskSize(storeId, size); + return (ret == DistributedDB::DBStatus::OK); +} + +void DbMetaCallbackDelegateMgr::GetKvStoreKeys(std::vector &dbStats) +{ + if (IsDestruct()) { + return; + } + DistributedDB::DBStatus dbStatusTmp; + Option option {.createIfNecessary = true, .isMemoryDb = false, .isEncryptedDb = false}; + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegatePtr = nullptr; + delegate_->GetKvStore( + Constant::SERVICE_META_DB_NAME, option, + [&kvStoreNbDelegatePtr, &dbStatusTmp](DistributedDB::DBStatus dbStatus, + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate) { + kvStoreNbDelegatePtr = kvStoreNbDelegate; + dbStatusTmp = dbStatus; + }); + + if (dbStatusTmp != DistributedDB::DBStatus::OK) { + return; + } + DistributedDB::Key dbKey = KvStoreMetaRow::GetKeyFor(""); + std::vector entries; + kvStoreNbDelegatePtr->GetEntries(dbKey, entries); + if (entries.empty()) { + delegate_->CloseKvStore(kvStoreNbDelegatePtr); + return; + } + for (auto const &entry : entries) { + std::string key = std::string(entry.key.begin(), entry.key.end()); + std::vector out; + Split(key, Constant::KEY_SEPARATOR, out); + if (out.size() >= VECTOR_SIZE) { + StoreInfo storeInfo = {out[USER_ID], out[APP_ID], out[STORE_ID]}; + dbStats.push_back(std::move(storeInfo)); + } + } + delegate_->CloseKvStore(kvStoreNbDelegatePtr); +} +} // namespace DistributedKv +} // namespace OHOS diff --git a/services/distributeddataservice/app/test/unittest/kvstore_data_service_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_data_service_test.cpp new file mode 100755 index 000000000..358fa00be --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_data_service_test.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. + */ + +#include +#include +#include +#include +#include "kvstore_client_death_observer.h" +#include "kvstore_data_service.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; + +class KvStoreDataServiceTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvStoreDataServiceTest::SetUpTestCase(void) +{} + +void KvStoreDataServiceTest::TearDownTestCase(void) +{} + +void KvStoreDataServiceTest::SetUp(void) +{} + +void KvStoreDataServiceTest::TearDown(void) +{} + +/** +* @tc.name: RegisterClientDeathObserver001 +* @tc.desc: register client death observer +* @tc.type: FUNC +* @tc.require: AR000CQDU2 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreDataServiceTest, RegisterClientDeathObserver001, TestSize.Level1) +{ + AppId appId; + appId.appId = "app0"; + + KvStoreDataService kvDataService; + Status status = kvDataService.RegisterClientDeathObserver(appId, new KvStoreClientDeathObserver()); + + EXPECT_EQ(status, Status::SUCCESS) << "RegisterClientDeathObserver failed"; +} diff --git a/services/distributeddataservice/app/test/unittest/kvstore_flowctrl_manager_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_flowctrl_manager_test.cpp new file mode 100755 index 000000000..e2e662be9 --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_flowctrl_manager_test.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 + +#include "flowctrl_manager/kvstore_flowctrl_manager.h" +#include +#include +#include "time_utils.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; + +class KvStoreFlowCtrlManagerTest : public testing::Test { +public: + static inline const int MANAGER_BURST_CAPACITY = 50; + static inline const int MANAGER_SUSTAINED_CAPACITY = 500; + static inline const int OPERATION_BURST_CAPACITY = 1000; + static inline const int OPERATION_SUSTAINED_CAPACITY = 10000; + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvStoreFlowCtrlManagerTest::SetUpTestCase(void) +{} + +void KvStoreFlowCtrlManagerTest::TearDownTestCase(void) +{} + +void KvStoreFlowCtrlManagerTest::SetUp(void) +{} + +void KvStoreFlowCtrlManagerTest::TearDown(void) +{} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest001 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest001, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 1001; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(1, arr[0]); + EXPECT_EQ(1000, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest002 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest002, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 1000; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(1000, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest003 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest003, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 999; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(999, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest004 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: SR000F3H5U AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest004, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 9999; i++) { + arr[ptr->IsTokenEnough()]++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(9999, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest005 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest005, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 10000; i++) { + arr[ptr->IsTokenEnough()]++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(10000, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest006 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest006, TestSize.Level1) +{ + auto ptr = std::make_shared(OPERATION_BURST_CAPACITY, OPERATION_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + uint64_t curTime = 0; + uint64_t lastTime = TimeUtils::CurrentTimeMicros(); + for (int i = 0; i < 10001; i++) { + arr[ptr->IsTokenEnough()]++; + while (true) { + curTime = TimeUtils::CurrentTimeMicros(); + if ((curTime - lastTime) > 1000) { + lastTime = curTime; + break; + } + } + } + EXPECT_EQ(1, arr[0]); + EXPECT_EQ(10000, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest007 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest007, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 51; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(1, arr[0]); + EXPECT_EQ(50, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest008 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest008, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 50; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(50, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest009 +* @tc.desc: burst flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP7 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest009, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 49; i++) { + arr[ptr->IsTokenEnough()]++; + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(49, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest010 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest010, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 499; i++) { + arr[ptr->IsTokenEnough()]++; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(499, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest011 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest011, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 500; i++) { + arr[ptr->IsTokenEnough()]++; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + EXPECT_EQ(0, arr[0]); + EXPECT_EQ(500, arr[1]); +} + +/** +* @tc.name: KvStoreFlowCtrlManagerTest012 +* @tc.desc: sustained flow control +* @tc.type: FUNC +* @tc.require: AR000F3OP8 +* @tc.author: jishengwu +*/ +HWTEST_F(KvStoreFlowCtrlManagerTest, KvStoreFlowCtrlManagerTest012, TestSize.Level1) +{ + auto ptr = std::make_shared(MANAGER_BURST_CAPACITY, MANAGER_SUSTAINED_CAPACITY); + int arr[2] = {0, 0}; + for (int i = 0; i < 501; i++) { + arr[ptr->IsTokenEnough()]++; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + EXPECT_EQ(1, arr[0]); + EXPECT_EQ(500, arr[1]); +} + diff --git a/services/distributeddataservice/app/test/unittest/kvstore_impl_logical_isolation_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_impl_logical_isolation_test.cpp new file mode 100755 index 000000000..e2ec2a8a2 --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_impl_logical_isolation_test.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kvstore_data_service.h" +#include "kvstore_impl.h" +#include "refbase.h" +#include "types.h" +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; + +sptr g_kvStoreDataService; +Options g_defaultOptions; +AppId g_appId; +StoreId g_storeId; +AppId g_appId1; +StoreId g_storeId1; +AppId g_appId2; +StoreId g_storeId2; + +class KvStoreImplLogicalIsolationTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void KvStoreImplLogicalIsolationTest::SetUpTestCase(void) +{ + g_defaultOptions.createIfMissing = true; + g_defaultOptions.encrypt = false; + g_defaultOptions.autoSync = true; + g_defaultOptions.kvStoreType = KvStoreType::MULTI_VERSION; + + g_appId.appId = "lgc0"; + g_storeId.storeId = "store0"; + + g_appId1.appId = "lgc1"; + g_storeId1.storeId = "store1"; + + g_appId2.appId = "lgc2"; + g_storeId2.storeId = "store2"; +} + +void KvStoreImplLogicalIsolationTest::TearDownTestCase(void) +{ + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} + +void KvStoreImplLogicalIsolationTest::SetUp(void) +{ + g_kvStoreDataService = sptr(new KvStoreDataService()); + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} + +void KvStoreImplLogicalIsolationTest::TearDown(void) +{ + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} + +/** +* @tc.name: LogicalIsolation001 +* @tc.desc: Verify getting KvStore successfully +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation001, TestSize.Level1) +{ + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); +} + +/** +* @tc.name: LogicalIsolation002 +* @tc.desc: get KvStore which exists +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation002, TestSize.Level1) +{ + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl EQ fail"; + + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); +} + +/** +* @tc.name: LogicalIsolation003 +* @tc.desc: get different KvStore with different g_appId and the same g_storeId +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation003, TestSize.Level1) +{ + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl NE fail"; + + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); + status = g_kvStoreDataService->CloseKvStore(g_appId1, g_storeId); +} + +/** +* @tc.name: LogicalIsolation004 +* @tc.desc: get different KvStore with the same g_appId and different g_storeId +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation004, TestSize.Level1) +{ + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl NE fail"; +} + +/** +* @tc.name: LogicalIsolation005 +* @tc.desc: get different KvStore with different g_appId and different g_storeId +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation005, TestSize.Level1) +{ + KvStoreDataService kvDataService; + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl NE fail"; +} + +/** +* @tc.name: LogicalIsolation006 +* @tc.desc: multithread create the same kvstore. +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation006, TestSize.Level1) +{ + sptr kvStorePtr; + sptr kvStorePtr1; + sptr kvStorePtr2; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + + std::thread thread1([&]() { + Status status1 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status1, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + + std::thread thread2([&]() { + Status status2 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr2 = std::move(kvStore); }); + EXPECT_EQ(status2, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + + thread1.join(); + thread2.join(); + + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr2, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl EQ fail"; + EXPECT_EQ(kvStorePtr, kvStorePtr2) << "Two KvStoreImpl EQ fail"; +} + +/** +* @tc.name: LogicalIsolation007 +* @tc.desc: multithread create different kvstore. +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS39 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplLogicalIsolationTest, LogicalIsolation007, TestSize.Level1) +{ + sptr kvStorePtr; + sptr kvStorePtr1; + sptr kvStorePtr2; + + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + + std::thread thread1([&]() { + Status status1 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status1, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + + std::thread thread2([&]() { + Status status2 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId2, g_storeId2, + [&](sptr kvStore) { kvStorePtr2 = std::move(kvStore); }); + EXPECT_EQ(status2, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + + thread1.join(); + thread2.join(); + + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr2, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl NE fail"; + EXPECT_NE(kvStorePtr, kvStorePtr2) << "Two KvStoreImpl NE fail"; +} diff --git a/services/distributeddataservice/app/test/unittest/kvstore_impl_physical_isolation_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_impl_physical_isolation_test.cpp new file mode 100755 index 000000000..8648bb304 --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_impl_physical_isolation_test.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "constant.h" +#include "kvstore_data_service.h" +#include "kvstore_impl.h" +#include "refbase.h" +#include "types.h" +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; + +sptr g_kvStoreDataService; +Options g_defaultOptions; +AppId g_appId; +StoreId g_storeId; +AppId g_appId1; +StoreId g_storeId1; +AppId g_appId2; +StoreId g_storeId2; + +class KvStoreImplPhysicalIsolationTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; +void KvStoreImplPhysicalIsolationTest::SetUpTestCase(void) +{ + g_defaultOptions.createIfMissing = true; + g_defaultOptions.encrypt = false; + g_defaultOptions.autoSync = true; + g_defaultOptions.kvStoreType = KvStoreType::MULTI_VERSION; + + g_appId.appId = "phy0"; + g_storeId.storeId = "store0"; + + g_appId1.appId = "phy1"; + g_storeId1.storeId = "store1"; + + g_appId2.appId = "phy2"; + g_storeId2.storeId = "store2"; +} +void KvStoreImplPhysicalIsolationTest::TearDownTestCase(void) +{ + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} + +void KvStoreImplPhysicalIsolationTest::SetUp(void) +{ + g_kvStoreDataService = sptr(new KvStoreDataService()); + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} +void KvStoreImplPhysicalIsolationTest::TearDown(void) +{ + g_kvStoreDataService->CloseAllKvStore(g_appId); + g_kvStoreDataService->CloseAllKvStore(g_appId1); + g_kvStoreDataService->CloseAllKvStore(g_appId2); + + g_kvStoreDataService->DeleteAllKvStore(g_appId); + g_kvStoreDataService->DeleteAllKvStore(g_appId1); + g_kvStoreDataService->DeleteAllKvStore(g_appId2); +} + +/** +* @tc.name: PhysicalIsolation001 +* @tc.desc: Verify the physical isolation function +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation001, TestSize.Level1) +{ + const std::string storePath = Constant::Concatenate({Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, "/", + Constant::GetDefaultHarmonyAccountName(), "/" }) + + std::string("phy0/store0"); + + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); +} +/** +* @tc.name: PhysicalIsolation002 +* @tc.desc: Verify the different store in the same app having different path +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation002, TestSize.Level1) +{ + const std::string deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + const std::string storePath1 = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store1"); + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath1) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId1); +} +/** +* @tc.name: PhysicalIsolation003 +* @tc.desc: Verify the different app with the same store having different path +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation003, TestSize.Level1) +{ + const std::string deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + const std::string storePath1 = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy1/store0"); + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath1) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); + status = g_kvStoreDataService->CloseKvStore(g_appId1, g_storeId); +} +/** +* @tc.name: PhysicalIsolation004 +* @tc.desc: Verify the different app with the different store having different path +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation004, TestSize.Level1) +{ + const std::string deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + + const std::string storePath1 = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy1/store1"); + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath1) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); + status = g_kvStoreDataService->CloseKvStore(g_appId1, g_storeId1); +} +/** +* @tc.name: PhysicalIsolation005 +* @tc.desc: Verify the same app with the same store having the same path +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A SR000CQS38 +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation005, TestSize.Level1) +{ + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, "/", + Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + + sptr kvStorePtr; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + + sptr kvStorePtr1; + status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + EXPECT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); +} +/** +* @tc.name: PhysicalIsolation006 +* @tc.desc: multithread create the same kvstore. +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation006, TestSize.Level1) +{ + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + AccountDelegate::MAIN_DEVICE_ACCOUNT_ID, "/", + Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + sptr kvStorePtr; + sptr kvStorePtr1; + sptr kvStorePtr2; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + ASSERT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + std::thread thread1([&]() { + Status status1 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + ASSERT_EQ(status1, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + std::thread thread2([&]() { + Status status2 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr2 = std::move(kvStore); }); + ASSERT_EQ(status2, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + thread1.join(); + thread2.join(); + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr2, nullptr) << "GetKvStore execute fail!!"; + EXPECT_EQ(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl EQ fail"; + EXPECT_EQ(kvStorePtr, kvStorePtr2) << "Two KvStoreImpl EQ fail"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + EXPECT_EQ((static_cast(kvStorePtr2.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); +} +/** +* @tc.name: PhysicalIsolation007 +* @tc.desc: multithread create different kvstore. +* @tc.type: FUNC +* @tc.require: AR000BVDF8 AR000CQS3A +* @tc.author: liuyuhui +*/ +HWTEST_F(KvStoreImplPhysicalIsolationTest, PhysicalIsolation007, TestSize.Level1) +{ + const std::string deviceAccountId = AccountDelegate::MAIN_DEVICE_ACCOUNT_ID; + const std::string storePath = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy0/store0"); + const std::string storePath1 = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy1/store1"); + const std::string storePath2 = Constant::Concatenate({ Constant::ROOT_PATH_CE, "/", Constant::SERVICE_NAME, "/", + deviceAccountId, "/", Constant::GetDefaultHarmonyAccountName(), "/" }) + std::string("phy2/store2"); + + sptr kvStorePtr; + sptr kvStorePtr1; + sptr kvStorePtr2; + Status status = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId, g_storeId, + [&](sptr kvStore) { kvStorePtr = std::move(kvStore); }); + ASSERT_EQ(status, Status::SUCCESS) << "GetKvStore return wrong status"; + std::thread thread1([&]() { + Status status1 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId1, g_storeId1, + [&](sptr kvStore) { kvStorePtr1 = std::move(kvStore); }); + ASSERT_EQ(status1, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + std::thread thread2([&]() { + Status status2 = g_kvStoreDataService->GetKvStore(g_defaultOptions, g_appId2, g_storeId2, + [&](sptr kvStore) { kvStorePtr2 = std::move(kvStore); }); + ASSERT_EQ(status2, Status::SUCCESS) << "GetKvStore return wrong status"; + }); + thread1.join(); + thread2.join(); + EXPECT_NE(kvStorePtr, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr1, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr2, nullptr) << "GetKvStore execute fail!!"; + EXPECT_NE(kvStorePtr, kvStorePtr1) << "Two KvStoreImpl NE fail"; + EXPECT_NE(kvStorePtr, kvStorePtr2) << "Two KvStoreImpl NE fail"; + EXPECT_EQ((static_cast(kvStorePtr.GetRefPtr()))->GetStorePath(), storePath) << "StorePath EQ fail"; + EXPECT_EQ((static_cast(kvStorePtr1.GetRefPtr()))->GetStorePath(), storePath1) << "StorePath EQ fail"; + EXPECT_EQ((static_cast(kvStorePtr2.GetRefPtr()))->GetStorePath(), storePath2) << "StorePath EQ fail"; + status = g_kvStoreDataService->CloseKvStore(g_appId, g_storeId); + status = g_kvStoreDataService->CloseKvStore(g_appId1, g_storeId1); + status = g_kvStoreDataService->CloseKvStore(g_appId2, g_storeId2); +} diff --git a/services/distributeddataservice/app/test/unittest/kvstore_sync_manager_test.cpp b/services/distributeddataservice/app/test/unittest/kvstore_sync_manager_test.cpp new file mode 100644 index 000000000..e1ddbba8f --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/kvstore_sync_manager_test.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kvstore_sync_manager.h" +#include "log_print.h" +#include "ut_kvstore_nb_delegate_impl.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; +using namespace OHOS; +using DistributedDB::UtKvStoreNbDelegateImpl; + +class KvStoreSyncManagerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + static const uint32_t CHECK_WAITING_TIME = 50000; // 50ms + + void CreateKvStorePair(bool isAutoSync, const std::string &storeId, + std::shared_ptr &kvStore, std::shared_ptr &kvNb, + std::shared_ptr &kvStore2, std::shared_ptr &kvNb2); +}; + +void KvStoreSyncManagerTest::SetUpTestCase(void) +{} + +void KvStoreSyncManagerTest::TearDownTestCase(void) +{} + +void KvStoreSyncManagerTest::SetUp(void) +{} + +void KvStoreSyncManagerTest::TearDown(void) +{} + +void KvStoreSyncManagerTest::CreateKvStorePair(bool isAutoSync, const std::string &storeId, + std::shared_ptr &kvStore, + std::shared_ptr &kvNb, + std::shared_ptr &kvStore2, + std::shared_ptr &kvNb2) +{ + kvNb = std::make_unique(storeId, "DevA"); + kvNb2 = std::make_unique(storeId, "DevB"); + kvNb->SetNeighbor(kvNb2); + kvNb2->SetNeighbor(kvNb); + + Options options = { .autoSync = isAutoSync }; + std::string userId = "syncManagerTest"; + std::string appId = "syncTest"; + std::string appDir = "syncTest"; + kvStore = std::make_unique(options, userId, appId, storeId, appDir, kvNb.get()); + kvStore2 = std::make_unique(options, userId, appId, storeId, appDir, kvNb2.get()); + return; +} + +/** +* @tc.name: KvStoreSyncManagerTest001 +* @tc.desc: realtime sync +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest001, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(false, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + + std::vector syncDevices; + syncDevices.push_back("devB"); + uint32_t syncDelayMs = 0; // ms + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore->Put(key1, value1); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); + + Key key2("key2"); + Value value2("value2"); + Value value2Tmp; + kvStore->Put(key2, value2); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(value2.ToString(), value2Tmp.ToString()); + + kvStore->Delete(key2); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); +} + +/** +* @tc.name: KvStoreSyncManagerTest002 +* @tc.desc: merge delayed sync +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest002, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(false, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + + std::vector syncDevices; + syncDevices.push_back("devB"); + uint32_t syncDelayMs = 200; // ms + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore->Put(key1, value1); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + Key key2("key2"); + Value value2("value2"); + Value value2Tmp; + kvStore->Put(key2, value2); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + kvStore->Delete(key2); + kvStore->Sync(syncDevices, SyncMode::PUSH, syncDelayMs); + usleep(CHECK_WAITING_TIME); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + usleep(syncDelayMs * 1000); + ret = kvStore2->Get("key1", value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); +} + +/** +* @tc.name: KvStoreSyncManagerTest003 +* @tc.desc: auto realtime sync +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest003, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(true, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + KvSyncParam syncDelay = { 0 }; // ms + sptr output; + kvStore->Control(KvControlCmd::SET_SYNC_PARAM, TransferTypeToByteArray(syncDelay), output); + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore->Put(key1, value1); + usleep(CHECK_WAITING_TIME); + kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); + + Key key2("key2"); + Value value2("value2"); + Value value2Tmp; + kvStore->Put(key2, value2); + usleep(CHECK_WAITING_TIME); + kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(value2.ToString(), value2Tmp.ToString()); + + kvStore->Delete(key2); + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); +} + +/** +* @tc.name: KvStoreSyncManagerTest004 +* @tc.desc: merge delayed auto sync +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest004, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(true, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + KvSyncParam syncDelay = { 200 }; // ms + sptr output; + kvStore->Control(KvControlCmd::SET_SYNC_PARAM, TransferTypeToByteArray(syncDelay), output); + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore->Put(key1, value1); + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + Key key2("key2"); + Value value2("value2"); + Value value2Tmp; + kvStore->Put(key2, value2); + usleep(CHECK_WAITING_TIME); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + kvStore->Delete(key2); + usleep(CHECK_WAITING_TIME); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + usleep(syncDelay.allowedDelayMs * 1000); + ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); + ret = kvStore2->Get(key2, value2Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); +} + +/** +* @tc.name: KvStoreSyncManagerTest005 +* @tc.desc: realtime sync first +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest005, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(false, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + + std::shared_ptr kvStore3; + std::shared_ptr kvNb3; + std::shared_ptr kvStore4; + std::shared_ptr kvNb4; + CreateKvStorePair(false, "syncTest3", kvStore3, kvNb3, kvStore4, kvNb4); + + std::vector syncDevices; + syncDevices.push_back("devB"); + uint32_t sync1DelayMs = 0; // ms + uint32_t sync3DelayMs = 100; // ms + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore3->Put(key1, value1); + kvStore3->Sync(syncDevices, SyncMode::PUSH, sync3DelayMs); + kvStore->Put(key1, value1); + kvStore->Sync(syncDevices, SyncMode::PUSH, sync1DelayMs); + + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); + ret = kvStore4->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + usleep(sync3DelayMs * 1000); + ret = kvStore4->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); +} + +/** +* @tc.name: KvStoreSyncManagerTest006 +* @tc.desc: merge multiple sync +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest006, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(true, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + KvSyncParam syncDelay = { 1000 }; // ms + sptr output; + kvStore->Control(KvControlCmd::SET_SYNC_PARAM, TransferTypeToByteArray(syncDelay), output); + + uint32_t batch = 200; // check count + uint32_t count = 1000; // entries + std::string prefix = "keyTest"; + std::vector data(2000); // 2k bytes + for (uint32_t i = 0; i < count; i++) { + Key key(prefix + std::to_string(i)); + data[0] = static_cast(i); + Value value(data); + kvStore->Put(key, value); + + if ((i % batch) == (batch - 1)) { + usleep(CHECK_WAITING_TIME); + std::vector entries; + Status ret1 = kvStore2->GetEntries(prefix, entries); + EXPECT_EQ(ret1, Status::KEY_NOT_FOUND); + } + } + + usleep(syncDelay.allowedDelayMs * 1000); + std::vector entries; + Status ret = kvStore2->GetEntries(prefix, entries); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(entries.size(), count); +} + +/** +* @tc.name: KvStoreSyncManagerTest007 +* @tc.desc: if realtime sync timeout, do not blocking delayed sync long time +* @tc.type: FUNC +* @tc.require: SR000DOGQE AR000DPUAN +* @tc.author: wangtao +*/ +HWTEST_F(KvStoreSyncManagerTest, KvStoreSyncManagerTest007, TestSize.Level1) +{ + std::shared_ptr kvStore; + std::shared_ptr kvNb; + std::shared_ptr kvStore2; + std::shared_ptr kvNb2; + CreateKvStorePair(false, "syncTest", kvStore, kvNb, kvStore2, kvNb2); + + std::shared_ptr kvStore3; + std::shared_ptr kvNb3; + std::shared_ptr kvStore4; + std::shared_ptr kvNb4; + CreateKvStorePair(false, "syncTest3", kvStore3, kvNb3, kvStore4, kvNb4); + + std::vector syncDevices; + syncDevices.push_back("devB"); + uint32_t sync1DelayMs = 0; // ms + uint32_t sync3DelayMs = 200; // ms + + Key key1("key1"); + Value value1("value1"); + Value value1Tmp; + kvStore->Put(key1, value1); + kvNb->SetSyncStatus(DistributedDB::TIME_OUT); + kvStore->Sync(syncDevices, SyncMode::PUSH, sync1DelayMs); + kvStore3->Put(key1, value1); + kvStore3->Sync(syncDevices, SyncMode::PUSH, sync3DelayMs); + + usleep(CHECK_WAITING_TIME); + Status ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + ret = kvStore4->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + usleep(sync3DelayMs * 1000); + ret = kvStore4->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + + usleep(300 * 1000); + ret = kvStore2->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::KEY_NOT_FOUND); + ret = kvStore4->Get(key1, value1Tmp); + EXPECT_EQ(ret, Status::SUCCESS); + EXPECT_EQ(value1.ToString(), value1Tmp.ToString()); +} diff --git a/services/distributeddataservice/app/test/unittest/uninstaller_test.cpp b/services/distributeddataservice/app/test/unittest/uninstaller_test.cpp new file mode 100755 index 000000000..48972cc05 --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/uninstaller_test.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. + */ + +#include +#include "uninstaller.h" + +using namespace testing::ext; +using namespace OHOS::DistributedKv; + +class UninstallerTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void UninstallerTest::SetUpTestCase(void) +{} + +void UninstallerTest::TearDownTestCase(void) +{} + +void UninstallerTest::SetUp(void) +{} + +void UninstallerTest::TearDown(void) +{} + +/** + * @tc.name: Test001 + * @tc.desc: test get uninstaller instance. + * @tc.type: FUNC + * @tc.require: SR000DOGUN AR000DPSE9 + * @tc.author: hongbo + */ +HWTEST_F(UninstallerTest, Test001, TestSize.Level0) +{ + auto &unin = Uninstaller::GetInstance(); + unin.Init(nullptr); +} diff --git a/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.cpp b/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.cpp new file mode 100755 index 000000000..193eca6cd --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ut_kvstore_nb_delegate_impl.h" +#include +#include +#include + +namespace DistributedDB { +UtKvStoreNbDelegateImpl::UtKvStoreNbDelegateImpl(const std::string &storeId, const std::string &deviceId) + : storeId_(storeId), deviceId_(deviceId), syncStatus_(OK) +{} + +UtKvStoreNbDelegateImpl::~UtKvStoreNbDelegateImpl() +{} + +bool UtKvStoreNbDelegateImpl::IsValidKey(const Key &key) +{ + if (key.size() > MAX_KEY_SIZE) { + return false; + } + + if (key.empty()) { + return false; + } + return true; +} + +bool UtKvStoreNbDelegateImpl::IsValidValue(const Value &value) +{ + if (value.size() > MAX_VALUE_SIZE) { + return false; + } + return true; +} + +DBStatus UtKvStoreNbDelegateImpl::Get(const Key &key, Value &value) const +{ + if (!IsValidKey(key)) { + return INVALID_ARGS; + } + const auto it = db_.find(key); + if (it != db_.end()) { + if (!it->second.isDeleted) { + value = it->second.value; + return OK; + } + } + return NOT_FOUND; +} + +DBStatus UtKvStoreNbDelegateImpl::GetEntries(const Key &keyPrefix, std::vector &entries) const +{ + if (keyPrefix.size() > MAX_KEY_SIZE) { + return INVALID_ARGS; + } + + size_t sizeOld = entries.size(); + for (const auto &data : db_) { + if (std::equal(keyPrefix.begin(), keyPrefix.end(), data.first.begin())) { + if (!data.second.isDeleted) { + Entry entry{ data.first, data.second.value }; + entries.push_back(entry); + } + } + } + return (entries.size() > sizeOld) ? OK : NOT_FOUND; +} + +DBStatus UtKvStoreNbDelegateImpl::GetEntries(const Key &keyPrefix, KvStoreResultSet *&resultSet) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::CloseResultSet(KvStoreResultSet *&resultSet) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Put(const Key &key, const Value &value) +{ + DbValue dbValue{ value, true, false, false }; + return DoPut(key, dbValue); +} + +DBStatus UtKvStoreNbDelegateImpl::DoPut(const Key &key, const DbValue &dbValue) +{ + if (!IsValidKey(key) || !IsValidValue(dbValue.value)) { + return INVALID_ARGS; + } + db_.insert_or_assign(key, dbValue); + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::PutBatch(const std::vector &entries) +{ + return NOT_SUPPORT; +} + +DBStatus UtKvStoreNbDelegateImpl::PutByOtherDevice(const Key &key, const Value &value) +{ + DbValue dbValue{ value, false, false, true }; + auto ret = DoPut(key, dbValue); + if (ret != OK) { + return ret; + } + + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Delete(const Key &key) +{ + return DoDelete(key, true); +} + +DBStatus UtKvStoreNbDelegateImpl::DoDelete(const Key &key, bool isLocalDelete) +{ + if (!IsValidKey(key)) { + return INVALID_ARGS; + } + auto it = db_.find(key); + if (it != db_.end()) { + it->second.isLocalPut = isLocalDelete; + it->second.isDeleted = true; + it->second.isSynced = !isLocalDelete; + return OK; + } + + return NOT_FOUND; +} + +DBStatus UtKvStoreNbDelegateImpl::DeleteBatch(const std::vector &keys) +{ + return NOT_SUPPORT; +} + +DBStatus UtKvStoreNbDelegateImpl::DeleteByOtherDevice(const Key &key) +{ + auto ret = DoDelete(key, false); + if (ret != OK) { + return ret; + } + + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::GetLocal(const Key &key, Value &value) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::PutLocal(const Key &key, const Value &value) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::DeleteLocal(const Key &key) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::RegisterObserver(const Key &key, unsigned int mode, KvStoreObserver *observer) +{ + if (key.size() > MAX_KEY_SIZE) { + return INVALID_ARGS; + } + + if (mode > OBSERVER_CHANGES_LOCAL_ONLY || mode < OBSERVER_CHANGES_NATIVE) { + return INVALID_ARGS; + } + + if (observer == nullptr) { + return INVALID_ARGS; + } + + std::vector::iterator it = find(observerMap_.begin(), observerMap_.end(), observer); + if (it != observerMap_.end()) { + return DB_ERROR; + } + + observerMap_.push_back(observer); + + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::UnRegisterObserver(const KvStoreObserver *observer) +{ + if (observer == nullptr) { + return INVALID_ARGS; + } + + std::vector::iterator it = find(observerMap_.begin(), observerMap_.end(), observer); + if (it == observerMap_.end()) { + return NOT_FOUND; + } + + observerMap_.erase(it); + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::StartTransaction() +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Commit() +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Rollback() +{ + return OK; +} + +std::string UtKvStoreNbDelegateImpl::GetStoreId() const +{ + return storeId_; +} + +void UtKvStoreNbDelegateImpl::SetSyncStatus(DBStatus status) +{ + syncStatus_ = status; +} + +void UtKvStoreNbDelegateImpl::SetNeighbor(const std::shared_ptr &neighbor) +{ + neighbor_ = neighbor; +} + +DBStatus UtKvStoreNbDelegateImpl::Sync( + const std::vector &devices, SyncMode, + const std::function &devicesMap)> &onComplete, bool) +{ + if (syncStatus_ != OK) { + return syncStatus_; + } + auto neighbor = neighbor_.lock(); + if (neighbor != nullptr) { + for (auto &it : db_) { + if (it.second.isLocalPut && (!it.second.isSynced)) { + if (!it.second.isDeleted) { + neighbor->PutByOtherDevice(it.first, it.second.value); + } else { + neighbor->DeleteByOtherDevice(it.first); + } + it.second.isSynced = true; + } + } + } + + if (onComplete) { + std::map deviceResults; + for (const auto &dev : devices) { + deviceResults.emplace(dev, syncStatus_); + } + onComplete(deviceResults); + } + + return syncStatus_; +} + +DBStatus UtKvStoreNbDelegateImpl::Pragma(PragmaCmd cmd, PragmaData ¶mData) +{ + if (cmd == AUTO_SYNC) { + if (paramData == nullptr) { + return INVALID_ARGS; + } + bool isSync = *(static_cast(paramData)); + if (isSync) { + std::vector devices; + return Sync(devices, SYNC_MODE_PUSH_ONLY, nullptr, false); + } + } + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::SetConflictNotifier(int conflictType, const KvStoreNbConflictNotifier ¬ifier) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Rekey(const CipherPassword &password) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Export(const std::string &filePath, const CipherPassword &passwd) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::Import(const std::string &filePath, const CipherPassword &passwd) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::RemoveDeviceData(const std::string &device) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::GetEntries(const Query &query, std::vector &entries) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::GetEntries(const Query &query, KvStoreResultSet *&resultSet) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::GetCount(const Query &query, int &count) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::GetLocalEntries(const Key &keyPrefix, std::vector &entries) const +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::PublishLocal(const Key &key, bool deleteLocal, bool updateTimestamp, + const KvStoreNbPublishOnConflict &onConflict) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::UnpublishToLocal(const Key &key, bool deletePublic, bool updateTimestamp) +{ + return OK; +} +DBStatus UtKvStoreNbDelegateImpl::PutLocalBatch(const std::vector &entries) +{ + return OK; +} + +DBStatus UtKvStoreNbDelegateImpl::DeleteLocalBatch(const std::vector &keys) +{ + return OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.h b/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.h new file mode 100755 index 000000000..19d80954e --- /dev/null +++ b/services/distributeddataservice/app/test/unittest/ut_kvstore_nb_delegate_impl.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 FAKE_KV_STORE_NB_DELEGATE_IMPL_H +#define FAKE_KV_STORE_NB_DELEGATE_IMPL_H + +#include +#include +#include +#include +#include +#include "kv_store_nb_delegate.h" + +namespace DistributedDB { +class UtKvStoreNbDelegateImpl : public KvStoreNbDelegate { +public: + UtKvStoreNbDelegateImpl(const std::string &storeId, const std::string &deviceId); + ~UtKvStoreNbDelegateImpl() override; + + 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 Delete(const Key &key) override; + DBStatus DeleteBatch(const std::vector &keys) override; + + // Local entry 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 + DB_API DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + bool wait) override; + + // Special pragma interface, see PragmaCmd and PragmaData, + DB_API DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) override; + + // Set the conflict notifier for getting the specified type conflict data. + DB_API DBStatus SetConflictNotifier(int conflictType, const KvStoreNbConflictNotifier ¬ifier) override; + + // Used to rekey the database. + DB_API DBStatus Rekey(const CipherPassword &password) override; + + DBStatus Export(const std::string &filePath, const CipherPassword &passwd) override; + 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; + + void SetSyncStatus(DBStatus status); + void SetNeighbor(const std::shared_ptr &neighbor); + 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 { return DBStatus::NOT_SUPPORT; }; + + // 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. + DBStatus SetRemotePushFinishedNotify(const RemotePushFinisheNotifier ¬ifier) override + { + return DBStatus::NOT_SUPPORT; + }; +private: + static constexpr size_t MAX_KEY_SIZE = 1024; // 1KB + static constexpr size_t MAX_VALUE_SIZE = 4 * 1024 * 1024; // 4MB + struct DbValue { + Value value{}; + bool isLocalPut{ true }; + bool isDeleted{ false }; + bool isSynced{ false }; + }; + + static bool IsValidKey(const Key &key); + static bool IsValidValue(const Value &value); + DBStatus PutByOtherDevice(const Key &key, const Value &value); + DBStatus DeleteByOtherDevice(const Key &key); + DBStatus DoPut(const Key &key, const DbValue &dbValue); + DBStatus DoDelete(const Key &key, bool isLocalDelete); + + std::string storeId_{}; + std::string deviceId_{}; + std::map db_{}; + std::vector observerMap_{}; + std::weak_ptr neighbor_{}; + DBStatus syncStatus_{ OK }; +}; +} // namespace DistributedDB + +#endif diff --git a/services/distributeddataservice/libs/distributeddb/BUILD.gn b/services/distributeddataservice/libs/distributeddb/BUILD.gn new file mode 100755 index 000000000..625289ce3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/BUILD.gn @@ -0,0 +1,220 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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", + "common/include", + "communicator/include", + "storage/include", + "storage/src", + "storage/src/multiver", + "storage/src/operation", + "storage/src/sqlite", + "storage/src/upgrader", + "syncer/include", + "syncer/src", + "//third_party/openssl/include/", + ] + + defines = [ + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USE_SQLITE_SYMBOLS", + "USING_HILOG_LOGGER", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + ] +} + +config("distrdb_public_config") { + visibility = [ "*:*" ] + include_dirs = [ + "interfaces/include", + "include", + ] +} + +group("build_module") { + deps = [ ":distributeddb" ] +} + +ohos_shared_library("distributeddb") { + sources = [ + "common/src/auto_launch.cpp", + "common/src/db_common.cpp", + "common/src/db_constant.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/runtime_context.cpp", + "common/src/runtime_context_impl.cpp", + "common/src/schema_object.cpp", + "common/src/schema_utils.cpp", + "common/src/semaphore.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/value_object.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/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", + "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/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/result_entries_window.cpp", + "storage/src/single_ver_natural_store_commit_notify_data.cpp", + "storage/src/sqlite/query_object.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_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_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_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_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/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/single_ver_data_sync.cpp", + "syncer/src/single_ver_data_sync_with_sliding_window.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/sliding_window_receiver.cpp", + "syncer/src/sliding_window_sender.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/flatbuffers:flatbuffers_mini", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + ] + + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_static", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + subsystem_name = "distributeddatamgr" +} diff --git a/services/distributeddataservice/libs/distributeddb/common/include/auto_launch.h b/services/distributeddataservice/libs/distributeddb/common/include/auto_launch.h new file mode 100755 index 000000000..fb48035bc --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/auto_launch.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "types_export.h" +#include "kv_store_observer.h" +#include "kvdb_properties.h" +#include "ikvdb_connection.h" +#include "icommunicator_aggregator.h" +#include "auto_launch_export.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, +}; + +struct AutoLaunchItem { + KvDBProperties properties; + AutoLaunchNotifier notifier; + KvStoreObserver *observer = nullptr; + int conflictType = 0; + KvStoreNbConflictNotifier conflictNotifier; + IKvDBConnection *conn = nullptr; + KvDBObserverHandle *observerHandle = nullptr; + bool isWriteOpenNotifiered = false; + AutoLaunchItemState state = AutoLaunchItemState::UN_INITIAL; + bool isDisable = false; + bool inObserver = false; +}; + +class AutoLaunch { +public: + AutoLaunch() = default; + + ~AutoLaunch(); + + DISABLE_COPY_ASSIGN_MOVE(AutoLaunch); + + void SetCommunicatorAggregator(ICommunicatorAggregator *aggregator); + + int EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + KvStoreObserver *observer, int conflictType, KvStoreNbConflictNotifier conflictNotifier); + + int DisableKvStoreAutoLaunch(const std::string &identifier); + + void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const; + + void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback); + + static int GetAutoLaunchProperties(const AutoLaunchParam ¶m, KvDBProperties &properties); + +private: + + int EnableKvStoreAutoLaunchParmCheck(AutoLaunchItem &autoLaunchItem, const std::string &identifier); + + int GetConnectionInEnable(AutoLaunchItem &autoLaunchItem, const std::string &identifier); + + IKvDBConnection *GetOneConnection(const KvDBProperties &properties, int &errCode); + + // we will return errCode, if errCode != E_OK + int CloseConnectionStrict(AutoLaunchItem &autoLaunchItem); + + // before ReleaseDatabaseConnection, if errCode != E_OK, we not return, we try close more + 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); + + void ConnectionLifeCycleCallbackTask(const std::string &identifier); + + void OnlineCallBackTask(); + + void GetDoOpenMap(std::map &doOpenMap); + + void GetConnInDoOpenMap(std::map &doOpenMap); + + void UpdateGlobalMap(std::map &doOpenMap); + + void ReceiveUnknownIdentifierCallBackTask(const std::string &identifier); + + void CloseNotifier(const AutoLaunchItem &autoLaunchItem); + + void ConnectionLifeCycleCallback(const std::string &identifier); + + void OnlineCallBack(const std::string &device, bool isConnect); + + int ReceiveUnknownIdentifierCallBack(const LabelType &label); + + int AutoLaunchExt(const std::string &identifier); + + void AutoLaunchExtTask(const std::string identifier, AutoLaunchItem autoLaunchItem); + + void ExtObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier); + + void ExtConnectionLifeCycleCallback(const std::string &identifier); + + void ExtConnectionLifeCycleCallbackTask(const std::string &identifier); + + int SetConflictNotifier(IKvDBConnection *conn, int conflictType, const KvStoreNbConflictNotifier ¬ifier); + + mutable std::mutex dataLock_; + mutable std::mutex communicatorLock_; + std::set onlineDevices_; + std::map autoLaunchItemMap_; + ICommunicatorAggregator *communicatorAggregator_ = nullptr; + std::condition_variable cv_; + + std::mutex extLock_; + AutoLaunchRequestCallback autoLaunchRequestCallback_; + std::map extItemMap_; +}; +} // namespace DistributedDB +#endif // AUTO_LAUNCH_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/db_common.h b/services/distributeddataservice/libs/distributeddb/common/include/db_common.h new file mode 100755 index 000000000..840a3c8af --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/db_common.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 DISTRIBUTEDDB_COMMON_H +#define DISTRIBUTEDDB_COMMON_H + +#include +#include +#include "db_types.h" +#include "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 void SetDatabaseIds(KvDBProperties &properties, const std::string &appId, const std::string &userId, + const std::string &storeId); +}; + +// Define short macro substitute for original long expression for convenience of using +#define VEC_TO_STR(x) DBCommon::VectorToHexString(x).c_str() +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_COMMON_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/db_constant.h b/services/distributeddataservice/libs/distributeddb/common/include/db_constant.h new file mode 100755 index 000000000..a2f2dbbc1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/db_constant.h @@ -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. + */ + +#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 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 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 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 +}; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_CONSTANT_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/db_errno.h b/services/distributeddataservice/libs/distributeddb/common/include/db_errno.h new file mode 100755 index 000000000..d0736fa6b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/db_errno.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 unfinished. +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_IGNOR_DATA = (E_BASE + 95); // ignore the data changed by other devices. +constexpr int E_FORBID_CACHEDB = (E_BASE + 96); // such after rekey can not check passwd due to file control. +// 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 +// 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 +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_ERRNO_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/db_types.h b/services/distributeddataservice/libs/distributeddb/common/include/db_types.h new file mode 100755 index 000000000..b9e7bc05c --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/db_types.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 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 +}; + +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, +}; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_TYPES_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/endian_convert.h b/services/distributeddataservice/libs/distributeddb/common/include/endian_convert.h new file mode 100755 index 000000000..97060c201 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/hash.h b/services/distributeddataservice/libs/distributeddb/common/include/hash.h new file mode 100755 index 000000000..d2ee53327 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/json_object.h b/services/distributeddataservice/libs/distributeddb/common/include/json_object.h new file mode 100755 index 000000000..1285b6279 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/json_object.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 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); + static uint32_t CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd); + + // Support default constructor, copy constructor and copy assignment + JsonObject() = default; + ~JsonObject() = default; + JsonObject(const JsonObject &); + JsonObject& operator=(const JsonObject &); + // Move constructor and move assignment is not need currently + JsonObject(JsonObject &&) = delete; + JsonObject& operator=(JsonObject &&) = delete; + + // 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; + // 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. If inPath already exist, + // 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; + int InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue); + // 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); + + 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/services/distributeddataservice/libs/distributeddb/common/include/log_print.h b/services/distributeddataservice/libs/distributeddb/common/include/log_print.h new file mode 100644 index 000000000..8cdffe88b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/log_print.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 DISTRIBUTEDDB_LOG_PRINT_H +#define DISTRIBUTEDDB_LOG_PRINT_H + +#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/services/distributeddataservice/libs/distributeddb/common/include/macro_utils.h b/services/distributeddataservice/libs/distributeddb/common/include/macro_utils.h new file mode 100755 index 000000000..dc36c2faa --- /dev/null +++ b/services/distributeddataservice/libs/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) \ + virtual 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/services/distributeddataservice/libs/distributeddb/common/include/notification_chain.h b/services/distributeddataservice/libs/distributeddb/common/include/notification_chain.h new file mode 100755 index 000000000..8c0d50b5a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/param_check_utils.h b/services/distributeddataservice/libs/distributeddb/common/include/param_check_utils.h new file mode 100755 index 000000000..7b7124814 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/param_check_utils.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 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); + + // 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, + SchemaObject &schemaObject, std::string &canonicalDir); +}; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_PARAM_CHECK_UTILS_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/parcel.h b/services/distributeddataservice/libs/distributeddb/common/include/parcel.h new file mode 100755 index 000000000..5d7b05ed4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/parcel.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 WriteInt(int data); + uint32_t ReadInt(int &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); +#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) { + isError_ = true; + 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 (isError_ || bufPtr_ == nullptr || stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &len, sizeof(uint32_t)); + if (errCode != EOK) { + 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"); + uint32_t len; + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(uint32_t) > totalLen_ || sizeof(T) > INT32_MAX) { + isError_ = true; + return 0; + } + len = *(reinterpret_cast(bufPtr_)); + len = NetToHost(len); + if (len > INT32_MAX) { + 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_) { + 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(); + static uint32_t GetIntLen(); + static uint32_t GetUInt32Len(); + static uint32_t GetUInt64Len(); + 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: + bool isError_ = false; + uint8_t *buf_ = nullptr; + uint8_t *bufPtr_ = nullptr; + uint64_t parcelLen_ = 0; + uint64_t totalLen_ = 0; +}; +} // namespace DistributedDB + +#endif // PARCEL_H + diff --git a/services/distributeddataservice/libs/distributeddb/common/include/performance_analysis.h b/services/distributeddataservice/libs/distributeddb/common/include/performance_analysis.h new file mode 100644 index 000000000..6d9ef7586 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/platform_specific.h b/services/distributeddataservice/libs/distributeddb/common/include/platform_specific.h new file mode 100755 index 000000000..b95beb866 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/platform_specific.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 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; +}; + +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); +} // namespace OS +} // namespace DistributedDB + +#endif // PLATFORM_SPECIFIC_H \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/common/include/ref_object.h b/services/distributeddataservice/libs/distributeddb/common/include/ref_object.h new file mode 100644 index 000000000..7fce0ea1e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/res_finalizer.h b/services/distributeddataservice/libs/distributeddb/common/include/res_finalizer.h new file mode 100755 index 000000000..ba9764de2 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/runtime_context.h b/services/distributeddataservice/libs/distributeddb/common/include/runtime_context.h new file mode 100755 index 000000000..34622e868 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/runtime_context.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "macro_utils.h" +#include "notification_chain.h" +#include "icommunicator_aggregator.h" +#include "iprocess_system_api_adapter.h" +#include "types_export.h" +#include "kv_store_observer.h" +#include "kvdb_properties.h" +#include "auto_launch_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; + +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; + + // 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, + KvStoreObserver *observer, int conflictType, KvStoreNbConflictNotifier conflictNotifier) = 0; + + virtual int DisableKvStoreAutoLaunch(const std::string &identifier) = 0; + + virtual void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const = 0; + + virtual void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) = 0; + + virtual NotificationChain::Listener *RegisterLockStatusLister(const LockStatusNotifier &action, int &errorCode) = 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; +protected: + RuntimeContext() = default; + virtual ~RuntimeContext() {} +}; +} // namespace DistributedDB + +#endif // RUNTIME_CONTEXT_H diff --git a/services/distributeddataservice/libs/distributeddb/common/include/schema_object.h b/services/distributeddataservice/libs/distributeddb/common/include/schema_object.h new file mode 100755 index 000000000..e13005cba --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/schema_object.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "value_object.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, + UNRECOGNIZED = 3, +}; + +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 +}; + +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; +}; + +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; +}; + +class SchemaObject { +public: + static std::string GetExtractFuncName(SchemaType inSchemaType); + static std::string GenerateExtractSQL(SchemaType inSchemaType, const FieldPath &inFieldpath, FieldType inFieldType, + uint32_t skipSize); + + // 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); + + // Support default constructor, copy constructor and copy assignment + SchemaObject(); + ~SchemaObject() = default; + SchemaObject(const SchemaObject &); + SchemaObject& operator=(const SchemaObject &); + // 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); + + bool IsSchemaValid() const; + SchemaType GetSchemaType() const; + // 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; + + 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: + 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/services/distributeddataservice/libs/distributeddb/common/include/schema_utils.h b/services/distributeddataservice/libs/distributeddb/common/include/schema_utils.h new file mode 100755 index 000000000..26f3a5452 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/schema_utils.h @@ -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. + */ + +#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 { +const std::string KEYWORD_SCHEMA_VERSION = "SCHEMA_VERSION"; +const std::string KEYWORD_SCHEMA_MODE = "SCHEMA_MODE"; +const std::string KEYWORD_SCHEMA_DEFINE = "SCHEMA_DEFINE"; +const std::string KEYWORD_SCHEMA_INDEXES = "SCHEMA_INDEXES"; +const std::string KEYWORD_SCHEMA_SKIPSIZE = "SCHEMA_SKIPSIZE"; +const std::string KEYWORD_INDEX = "INDEX"; // For FlatBuffer-Schema + +const std::string KEYWORD_MODE_STRICT = "STRICT"; +const std::string KEYWORD_MODE_COMPATIBLE = "COMPATIBLE"; + +const std::string KEYWORD_TYPE_BOOL = "BOOL"; +const std::string KEYWORD_TYPE_INTEGER = "INTEGER"; +const std::string KEYWORD_TYPE_LONG = "LONG"; +const std::string KEYWORD_TYPE_DOUBLE = "DOUBLE"; +const std::string KEYWORD_TYPE_STRING = "STRING"; + +const std::string KEYWORD_ATTR_NOT_NULL = "NOT NULL"; +const std::string KEYWORD_ATTR_DEFAULT = "DEFAULT"; +const std::string KEYWORD_ATTR_VALUE_NULL = "null"; +const std::string KEYWORD_ATTR_VALUE_TRUE = "true"; +const std::string KEYWORD_ATTR_VALUE_FALSE = "false"; + +const uint32_t SCHEMA_META_FEILD_COUNT_MAX = 5; +const uint32_t SCHEMA_META_FEILD_COUNT_MIN = 3; +const uint32_t SCHEMA_FEILD_NAME_LENGTH_MAX = 64; +const uint32_t SCHEMA_FEILD_NAME_LENGTH_MIN = 1; +const uint32_t SCHEMA_FEILD_NAME_COUNT_MAX = 256; +const uint32_t SCHEMA_FEILD_NAME_COUNT_MIN = 1; +const uint32_t SCHEMA_FEILD_PATH_DEPTH_MAX = 4; +const uint32_t SCHEMA_INDEX_COUNT_MAX = 32; +const uint32_t SCHEMA_STRING_SIZE_LIMIT = 524288; // 512K +const uint32_t SCHEMA_DEFAULT_STRING_SIZE_LIMIT = 4096; // 4K +const uint32_t SCHEMA_SKIPSIZE_MAX = 4194302; // 4M - 2 Bytes +const std::string SCHEMA_SUPPORT_VERSION = "1.0"; + +const uint32_t SECURE_BYTE_ALIGN = 8; // 8 bytes align + +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. + static int ParseAndCheckSchemaAttribute(const std::string &inAttrString, SchemaAttribute &outAttr); + + // 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. + static int ParseAndCheckFieldPath(const std::string &inPathString, FieldPath &outPath); + + // 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); + + 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/services/distributeddataservice/libs/distributeddb/common/include/semaphore.h b/services/distributeddataservice/libs/distributeddb/common/include/semaphore.h new file mode 100755 index 000000000..6372363f0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/semaphore.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_H +#define SEMAPHORE_H + +#include +#include +#include +#include +#include "macro_utils.h" + +namespace DistributedDB { +class Semaphore { +public: + explicit Semaphore(int count); + ~Semaphore(); + + // Delete the copy and assign constructors + DISABLE_COPY_ASSIGN_MOVE(Semaphore); + + 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/services/distributeddataservice/libs/distributeddb/common/include/task_pool.h b/services/distributeddataservice/libs/distributeddb/common/include/task_pool.h new file mode 100644 index 000000000..fe5d8c8bc --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/value_hash_calc.h b/services/distributeddataservice/libs/distributeddb/common/include/value_hash_calc.h new file mode 100755 index 000000000..2a02ccc5e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/include/value_object.h b/services/distributeddataservice/libs/distributeddb/common/include/value_object.h new file mode 100755 index 000000000..2071c0662 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/value_object.h @@ -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. + */ + +#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/services/distributeddataservice/libs/distributeddb/common/include/version.h b/services/distributeddataservice/libs/distributeddb/common/include/version.h new file mode 100755 index 000000000..4ee201df8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/include/version.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 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.3"; // 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 second released version +constexpr uint32_t SOFTWARE_VERSION_EARLIEST = SOFTWARE_VERSION_RELEASE_1_0; +constexpr uint32_t SOFTWARE_VERSION_CURRENT = SOFTWARE_VERSION_RELEASE_3_0; +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/services/distributeddataservice/libs/distributeddb/common/src/auto_launch.cpp b/services/distributeddataservice/libs/distributeddb/common/src/auto_launch.cpp new file mode 100755 index 000000000..a1bc6fb68 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/auto_launch.cpp @@ -0,0 +1,910 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "log_print.h" +#include "runtime_context.h" +#include "kvdb_pragma.h" +#include "kvdb_manager.h" +#include "kv_store_changed_data_impl.h" +#include "sync_able_kvdb_connection.h" +#include "semaphore.h" +#include "kv_store_nb_conflict_data_impl.h" +#include "db_common.h" +#include "param_check_utils.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), 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; + } + } + + std::set inDisableSet; + std::set inWaitIdleSet; + std::unique_lock autoLock(dataLock_); + for (auto &iter : autoLaunchItemMap_) { + if (iter.second.isDisable) { + inDisableSet.insert(iter.first); + } else if (iter.second.state == AutoLaunchItemState::IDLE && (!iter.second.inObserver)) { + TryCloseConnection(iter.second); + } else { + inWaitIdleSet.insert(iter.first); + iter.second.isDisable = true; + } + } + for (const auto &identifier : inDisableSet) { + cv_.wait(autoLock, [identifier, this] { + return autoLaunchItemMap_.count(identifier) == 0 || (!autoLaunchItemMap_[identifier].isDisable); + }); + if (autoLaunchItemMap_.count(identifier) != 0) { + TryCloseConnection(autoLaunchItemMap_[identifier]); + } + } + for (const auto &identifier : inWaitIdleSet) { + cv_.wait(autoLock, [identifier, this] { + return (autoLaunchItemMap_[identifier].state == AutoLaunchItemState::IDLE) && + (!autoLaunchItemMap_[identifier].inObserver); + }); + TryCloseConnection(autoLaunchItemMap_[identifier]); + } +} + +int AutoLaunch::EnableKvStoreAutoLaunchParmCheck(AutoLaunchItem &autoLaunchItem, const std::string &identifier) +{ + if (identifier.empty()) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is invalid"); + return -E_INVALID_ARGS; + } + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) != 0) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck identifier is already enabled!"); + return -E_ALREADY_SET; + } + if (autoLaunchItemMap_.size() == MAX_AUTO_LAUNCH_ITEM_NUM) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck size is max(8) now"); + return -E_MAX_LIMITS; + } + autoLaunchItem.state = AutoLaunchItemState::IN_ENABLE; + autoLaunchItemMap_[identifier] = autoLaunchItem; + LOGI("[AutoLaunch] EnableKvStoreAutoLaunchParmCheck insert map first"); + return E_OK; +} + +int AutoLaunch::EnableKvStoreAutoLaunch(const KvDBProperties &properties, AutoLaunchNotifier notifier, + KvStoreObserver *observer, int conflictType, KvStoreNbConflictNotifier conflictNotifier) +{ + LOGI("[AutoLaunch] EnableKvStoreAutoLaunch"); + std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + AutoLaunchItem autoLaunchItem{properties, notifier, observer, conflictType, conflictNotifier}; + int errCode = EnableKvStoreAutoLaunchParmCheck(autoLaunchItem, identifier); + if (errCode != E_OK) { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunch failed errCode:%d", errCode); + return errCode; + } + errCode = GetConnectionInEnable(autoLaunchItem, identifier); + if (errCode == E_OK) { + LOGI("[AutoLaunch] EnableKvStoreAutoLaunch ok"); + } else { + LOGE("[AutoLaunch] EnableKvStoreAutoLaunch failed errCode:%d", errCode); + } + return errCode; +} + +int AutoLaunch::GetConnectionInEnable(AutoLaunchItem &autoLaunchItem, const std::string &identifier) +{ + LOGI("[AutoLaunch] GetConnectionInEnable"); + int errCode; + autoLaunchItem.conn = KvDBManager::GetDatabaseConnection(autoLaunchItem.properties, errCode, false); + if (errCode == -E_ALREADY_OPENED) { + LOGI("[AutoLaunch] GetConnectionInEnable user already getkvstore by self"); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + return E_OK; + } + if (autoLaunchItem.conn == nullptr) { + LOGE("[AutoLaunch] GetConnectionInEnable GetDatabaseConnection errCode:%d", errCode); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_.erase(identifier); + return errCode; + } + if (onlineDevices_.empty()) { + LOGI("[AutoLaunch] GetConnectionInEnable no online device, ReleaseDatabaseConnection"); + errCode = KvDBManager::ReleaseDatabaseConnection(autoLaunchItem.conn); + if (errCode != E_OK) { + LOGE("[AutoLaunch] GetConnectionInEnable ReleaseDatabaseConnection failed errCode:%d", errCode); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_.erase(identifier); + return errCode; + } + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + return E_OK; + } + errCode = RegisterObserverAndLifeCycleCallback(autoLaunchItem, identifier, false); + if (errCode == E_OK) { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + autoLaunchItemMap_[identifier].conn = autoLaunchItem.conn; + autoLaunchItemMap_[identifier].observerHandle = autoLaunchItem.observerHandle; + LOGI("[AutoLaunch] GetConnectionInEnable RegisterObserverAndLifeCycleCallback ok"); + } else { + LOGE("[AutoLaunch] GetConnectionInEnable RegisterObserverAndLifeCycleCallback err, do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do nothing if failed + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_.erase(identifier); + } + 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; + } + int errCode = autoLaunchItem.conn->RegisterLifeCycleCallback(nullptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] CloseConnectionStrict RegisterLifeCycleCallback failed errCode:%d", errCode); + return errCode; + } + if (autoLaunchItem.observerHandle != nullptr) { + errCode = autoLaunchItem.conn->UnRegisterObserver(autoLaunchItem.observerHandle); + if (errCode != E_OK) { + LOGE("[AutoLaunch] CloseConnectionStrict UnRegisterObserver failed errCode:%d", errCode); + return errCode; + } + autoLaunchItem.observerHandle = nullptr; + } + errCode = KvDBManager::ReleaseDatabaseConnection(autoLaunchItem.conn); + 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"); + if (autoLaunchItem.conn == nullptr) { + LOGI("[AutoLaunch] TryCloseConnection conn is nullptr, do nothing"); + return; + } + int errCode = autoLaunchItem.conn->RegisterLifeCycleCallback(nullptr); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseConnection RegisterLifeCycleCallback failed errCode:%d", errCode); + } + if (autoLaunchItem.observerHandle != nullptr) { + errCode = autoLaunchItem.conn->UnRegisterObserver(autoLaunchItem.observerHandle); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseConnection UnRegisterObserver failed errCode:%d", errCode); + } + autoLaunchItem.observerHandle = nullptr; + } + errCode = KvDBManager::ReleaseDatabaseConnection(autoLaunchItem.conn); + if (errCode != E_OK) { + LOGE("[AutoLaunch] TryCloseConnection ReleaseDatabaseConnection failed errCode:%d", errCode); + } +} + +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"); + if (isExt) { + errCode = autoLaunchItem.conn->RegisterLifeCycleCallback(std::bind( + &AutoLaunch::ExtConnectionLifeCycleCallback, this, std::placeholders::_1)); + } else { + errCode = autoLaunchItem.conn->RegisterLifeCycleCallback(std::bind(&AutoLaunch::ConnectionLifeCycleCallback, + this, std::placeholders::_1)); + } + if (errCode != E_OK) { + LOGE("[AutoLaunch] RegisterLifeCycleCallback failed, errCode:%d", errCode); + return errCode; + } + LOGI("[AutoLaunch] RegisterLifeCycleCallback ok"); + + errCode = SetConflictNotifier(autoLaunchItem.conn, autoLaunchItem.conflictType, autoLaunchItem.conflictNotifier); + if (errCode != E_OK) { + LOGE("[AutoLaunch] SetConflictNotifier failed, errCode:%d", errCode); + return errCode; + } + + bool enAutoSync = true; + 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] PRAGMA_AUTO_SYNC ok"); + return errCode; +} + +int AutoLaunch::RegisterObserver(AutoLaunchItem &autoLaunchItem, const std::string &identifier, bool isExt) +{ + LOGI("[AutoLaunch] RegisterObserver"); + if (autoLaunchItem.conn == nullptr) { + LOGE("[AutoLaunch] autoLaunchItem.conn is nullptr"); + return -E_INTERNAL_ERROR; + } + int errCode; + Key key; + KvDBObserverHandle *observerHandle = nullptr; + if (isExt) { + observerHandle = autoLaunchItem.conn->RegisterObserver(OBSERVER_CHANGES_FOREIGN, key, + std::bind(&AutoLaunch::ExtObserverFunc, this, std::placeholders::_1, identifier), errCode); + } else { + observerHandle = autoLaunchItem.conn->RegisterObserver(OBSERVER_CHANGES_FOREIGN, key, + std::bind(&AutoLaunch::ObserverFunc, this, std::placeholders::_1, identifier), 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) +{ + LOGD("[AutoLaunch] ObserverFunc"); + AutoLaunchItem autoLaunchItem; + std::string userId; + std::string appId; + std::string storeId; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0) { + LOGE("[AutoLaunch] ObserverFunc err no this identifier in map"); + return; + } + if (autoLaunchItemMap_[identifier].isDisable) { + LOGI("[AutoLaunch] ObserverFunc isDisable, do nothing"); + return; + } + autoLaunchItemMap_[identifier].inObserver = true; + autoLaunchItem.observer = autoLaunchItemMap_[identifier].observer; + autoLaunchItem.isWriteOpenNotifiered = autoLaunchItemMap_[identifier].isWriteOpenNotifiered; + autoLaunchItem.notifier = autoLaunchItemMap_[identifier].notifier; + userId = autoLaunchItemMap_[identifier].properties.GetStringProp(KvDBProperties::USER_ID, ""); + appId = autoLaunchItemMap_[identifier].properties.GetStringProp(KvDBProperties::APP_ID, ""); + storeId = autoLaunchItemMap_[identifier].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.isWriteOpenNotifiered:%d", autoLaunchItem.isWriteOpenNotifiered); + + if (!autoLaunchItem.isWriteOpenNotifiered && autoLaunchItem.notifier != nullptr) { + { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].isWriteOpenNotifiered = 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].inObserver = false; + cv_.notify_all(); + LOGI("[AutoLaunch] ObserverFunc finished"); +} + +int AutoLaunch::DisableKvStoreAutoLaunch(const std::string &identifier) +{ + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch"); + AutoLaunchItem autoLaunchItem; + { + std::unique_lock autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0) { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch identifier is not exist!"); + return -E_NOT_FOUND; + } + if (autoLaunchItemMap_[identifier].isDisable == true) { + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch already disabling in another thread, do nothing here"); + return -E_BUSY; + } + if (autoLaunchItemMap_[identifier].state == AutoLaunchItemState::IN_ENABLE) { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch enable not return, do not disable!"); + return -E_BUSY; + } + autoLaunchItemMap_[identifier].isDisable = true; + if (autoLaunchItemMap_[identifier].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch wait idle"); + cv_.wait(autoLock, [identifier, this] { + return (autoLaunchItemMap_[identifier].state == AutoLaunchItemState::IDLE) && + (!autoLaunchItemMap_[identifier].inObserver); + }); + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch wait idle ok"); + } + autoLaunchItem = autoLaunchItemMap_[identifier]; + } + + int errCode = CloseConnectionStrict(autoLaunchItem); + if (errCode == E_OK) { + std::unique_lock autoLock(dataLock_); + autoLaunchItemMap_.erase(identifier); + cv_.notify_all(); + LOGI("[AutoLaunch] DisableKvStoreAutoLaunch CloseConnection ok"); + } else { + LOGE("[AutoLaunch] DisableKvStoreAutoLaunch CloseConnection failed errCode:%d", errCode); + std::unique_lock autoLock(dataLock_); + autoLaunchItemMap_[identifier].isDisable = false; + autoLaunchItemMap_[identifier].observerHandle = autoLaunchItem.observerHandle; + cv_.notify_all(); + return errCode; + } + if (autoLaunchItem.isWriteOpenNotifiered && autoLaunchItem.notifier) { + RuntimeContext::GetInstance()->ScheduleTask([autoLaunchItem, this] { 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.properties.GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = autoLaunchItem.properties.GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.properties.GetStringProp(KvDBProperties::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) +{ + LOGI("[AutoLaunch] ConnectionLifeCycleCallbackTask"); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0) { + LOGE("[AutoLaunch] ConnectionLifeCycleCallback identifier is not exist!"); + return; + } + if (autoLaunchItemMap_[identifier].isDisable) { + LOGI("[AutoLaunch] ConnectionLifeCycleCallback isDisable, do nothing"); + return; + } + if (autoLaunchItemMap_[identifier].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] ConnectionLifeCycleCallback state:%d is not idle, do nothing", + autoLaunchItemMap_[identifier].state); + return; + } + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IN_LIFE_CYCLE_CALL_BACK; + autoLaunchItem = autoLaunchItemMap_[identifier]; + } + LOGI("[AutoLaunch] ConnectionLifeCycleCallbackTask do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do onthing if failed + LOGI("[AutoLaunch] ConnectionLifeCycleCallback do CloseConnection finished"); + { + std::lock_guard lock(dataLock_); + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + autoLaunchItemMap_[identifier].conn = nullptr; + autoLaunchItemMap_[identifier].isWriteOpenNotifiered = false; + cv_.notify_all(); + LOGI("[AutoLaunch] ConnectionLifeCycleCallback notify_all"); + } + if (autoLaunchItem.isWriteOpenNotifiered) { + CloseNotifier(autoLaunchItem); + } +} + +void AutoLaunch::ConnectionLifeCycleCallback(const std::string &identifier) +{ + LOGI("[AutoLaunch] ConnectionLifeCycleCallback"); + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&AutoLaunch::ConnectionLifeCycleCallbackTask, + this, identifier)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ConnectionLifeCycleCallback ScheduleTask failed"); + } +} + +IKvDBConnection *AutoLaunch::GetOneConnection(const KvDBProperties &properties, int &errCode) +{ + LOGI("[AutoLaunch] GetOneConnection"); + 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); + } + return conn; +} + +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 &iter : autoLaunchItemMap_) { + 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", iter.second.state); + continue; + } else if (iter.second.conn != nullptr) { + LOGI("[AutoLaunch] GetDoOpenMap this item is opened"); + continue; + } else { + doOpenMap[iter.first] = iter.second; + iter.second.state = AutoLaunchItemState::IN_COMMUNICATOR_CALL_BACK; + LOGI("[AutoLaunch] GetDoOpenMap set state IN_COMMUNICATOR_CALL_BACK"); + } + } +} + +void AutoLaunch::GetConnInDoOpenMap(std::map &doOpenMap) +{ + LOGI("[AutoLaunch] GetConnInDoOpenMap doOpenMap.size():%llu", doOpenMap.size()); + if (doOpenMap.empty()) { + return; + } + Semaphore sema(1 - doOpenMap.size()); + for (auto &iter : doOpenMap) { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([&sema, &iter, this] { + int errCode; + iter.second.conn = GetOneConnection(iter.second.properties, errCode); + LOGI("[AutoLaunch] GetConnInDoOpenMap GetOneConnection errCode:%d\n", errCode); + if (iter.second.conn == nullptr) { + sema.SendSemaphore(); + LOGI("[AutoLaunch] GetConnInDoOpenMap in open thread finish SendSemaphore"); + return; + } + errCode = RegisterObserverAndLifeCycleCallback(iter.second, iter.first, false); + if (errCode != 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 &iter : doOpenMap) { + if (iter.second.conn != nullptr) { + autoLaunchItemMap_[iter.first].conn = iter.second.conn; + autoLaunchItemMap_[iter.first].observerHandle = iter.second.observerHandle; + autoLaunchItemMap_[iter.first].isWriteOpenNotifiered = false; + LOGI("[AutoLaunch] UpdateGlobalMap opened conn update map"); + } + autoLaunchItemMap_[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) +{ + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask"); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(dataLock_); + autoLaunchItem = autoLaunchItemMap_[identifier]; + } + int errCode; + autoLaunchItem.conn = GetOneConnection(autoLaunchItem.properties, errCode); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack GetOneConnection errCode:%d\n", errCode); + if (autoLaunchItem.conn == nullptr) { + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].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].state = AutoLaunchItemState::IDLE; + cv_.notify_all(); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask set state IDLE"); + return; + } + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].conn = autoLaunchItem.conn; + autoLaunchItemMap_[identifier].observerHandle = autoLaunchItem.observerHandle; + autoLaunchItemMap_[identifier].isWriteOpenNotifiered = false; + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + cv_.notify_all(); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBackTask conn opened set state IDLE"); +} + +int AutoLaunch::ReceiveUnknownIdentifierCallBack(const LabelType &label) +{ + const std::string identifier(label.begin(), label.end()); + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack"); + int errCode; + { + std::lock_guard autoLock(dataLock_); + if (autoLaunchItemMap_.count(identifier) == 0) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack not find identifier"); + goto EXT; + } else if (autoLaunchItemMap_[identifier].isDisable) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack isDisable ,do nothing"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } else if (autoLaunchItemMap_[identifier].conn != nullptr) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack conn is not nullptr"); + return E_OK; + } else if (autoLaunchItemMap_[identifier].state != AutoLaunchItemState::IDLE) { + LOGI("[AutoLaunch] ReceiveUnknownIdentifierCallBack state:%d is not idle, do nothing", + autoLaunchItemMap_[identifier].state); + return E_OK; + } + autoLaunchItemMap_[identifier].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)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ReceiveUnknownIdentifierCallBack ScheduleTask failed"); + std::lock_guard autoLock(dataLock_); + autoLaunchItemMap_[identifier].state = AutoLaunchItemState::IDLE; + } + return errCode; + +EXT: + return AutoLaunchExt(identifier); +} + +void AutoLaunch::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) +{ + LOGI("[AutoLaunch] SetAutoLaunchRequestCallback"); + std::lock_guard lock(extLock_); + autoLaunchRequestCallback_ = callback; +} + +int AutoLaunch::AutoLaunchExt(const std::string &identifier) +{ + AutoLaunchParam param; + { + std::lock_guard lock(extLock_); + if (!autoLaunchRequestCallback_) { + LOGI("[AutoLaunch] autoLaunchRequestCallback_ is nullptr"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } + bool needOpen = autoLaunchRequestCallback_(identifier, param); + if (!needOpen) { + LOGI("[AutoLaunch] autoLaunchRequestCallback_ is not need open"); + return -E_NOT_FOUND; // not E_OK is ok for communicator + } + } + KvDBProperties properties; + int errCode = AutoLaunch::GetAutoLaunchProperties(param, properties); + 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{properties, param.notifier, param.option.observer, param.option.conflictType, + param.option.notifier}; + errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&AutoLaunch::AutoLaunchExtTask, this, + identifier, autoLaunchItem)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExt ScheduleTask errCode:%d", errCode); + } + return errCode; +} + +void AutoLaunch::AutoLaunchExtTask(const std::string identifier, AutoLaunchItem autoLaunchItem) +{ + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) != 0) { + LOGE("[AutoLaunch] extItemMap_ has this identifier"); + return; + } + extItemMap_[identifier] = autoLaunchItem; + } + int errCode; + autoLaunchItem.conn = GetOneConnection(autoLaunchItem.properties, errCode); + LOGI("[AutoLaunch] AutoLaunchExtTask GetOneConnection errCode:%d", errCode); + if (autoLaunchItem.conn == nullptr) { + std::lock_guard autoLock(extLock_); + extItemMap_.erase(identifier); + return; + } + + errCode = RegisterObserverAndLifeCycleCallback(autoLaunchItem, identifier, true); + if (errCode != E_OK) { + LOGE("[AutoLaunch] AutoLaunchExtTask RegisterObserverAndLifeCycleCallback failed"); + TryCloseConnection(autoLaunchItem); // if here failed, do nothing + std::lock_guard autoLock(extLock_); + extItemMap_.erase(identifier); + return; + } + std::lock_guard autoLock(extLock_); + extItemMap_[identifier].conn = autoLaunchItem.conn; + extItemMap_[identifier].observerHandle = autoLaunchItem.observerHandle; + extItemMap_[identifier].isWriteOpenNotifiered = false; + LOGI("[AutoLaunch] AutoLaunchExtTask ok"); +} + +void AutoLaunch::ExtObserverFunc(const KvDBCommitNotifyData ¬ifyData, const std::string &identifier) +{ + LOGD("[AutoLaunch] ExtObserverFunc"); + AutoLaunchItem autoLaunchItem; + AutoLaunchNotifier notifier; + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) == 0) { + LOGE("[AutoLaunch] ExtObserverFunc this identifier not in map"); + return; + } + autoLaunchItem = extItemMap_[identifier]; + } + 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].isWriteOpenNotifiered && + autoLaunchItem.notifier != nullptr) { + extItemMap_[identifier].isWriteOpenNotifiered = true; + notifier = autoLaunchItem.notifier; + } else { + return; + } + } + + std::string userId = autoLaunchItem.properties.GetStringProp(KvDBProperties::USER_ID, ""); + std::string appId = autoLaunchItem.properties.GetStringProp(KvDBProperties::APP_ID, ""); + std::string storeId = autoLaunchItem.properties.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) +{ + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallback"); + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind( + &AutoLaunch::ExtConnectionLifeCycleCallbackTask, this, identifier)); + if (errCode != E_OK) { + LOGE("[AutoLaunch] ExtConnectionLifeCycleCallback ScheduleTask failed"); + } +} + +void AutoLaunch::ExtConnectionLifeCycleCallbackTask(const std::string &identifier) +{ + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallbackTask"); + AutoLaunchItem autoLaunchItem; + { + std::lock_guard autoLock(extLock_); + if (extItemMap_.count(identifier) == 0) { + LOGE("[AutoLaunch] ExtConnectionLifeCycleCallbackTask identifier is not exist!"); + return; + } + autoLaunchItem = extItemMap_[identifier]; + } + LOGI("[AutoLaunch] ExtConnectionLifeCycleCallbackTask do CloseConnection"); + TryCloseConnection(autoLaunchItem); // do nothing if failed + { + std::lock_guard lock(extLock_); + autoLaunchItem = extItemMap_[identifier]; + extItemMap_.erase(identifier); + } + if (autoLaunchItem.isWriteOpenNotifiered) { + CloseNotifier(autoLaunchItem); + } +} + +int AutoLaunch::SetConflictNotifier(IKvDBConnection *conn, int conflictType, const KvStoreNbConflictNotifier ¬ifier) +{ + if (conflictType == 0) { + return E_OK; + } + 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) != 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, KvDBProperties &properties) +{ + SchemaObject schemaObject; + std::string canonicalDir; + int errCode = ParamCheckUtils::CheckAndTransferAutoLaunchParam(param, schemaObject, canonicalDir); + if (errCode != E_OK) { + return errCode; + } + + if (param.option.isEncryptedDb) { + properties.SetPassword(param.option.cipher, param.option.passwd); + } + properties.SetStringProp(KvDBProperties::DATA_DIR, canonicalDir); + properties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, param.option.createIfNecessary); + properties.SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, param.option.createDirByStoreIdOnly); + properties.SetBoolProp(KvDBProperties::MEMORY_MODE, false); + properties.SetBoolProp(KvDBProperties::ENCRYPTED_MODE, param.option.isEncryptedDb); + properties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::SINGLE_VER_TYPE); + properties.SetSchema(schemaObject); + if (RuntimeContext::GetInstance()->IsProcessSystemApiAdapterValid()) { + properties.SetIntProp(KvDBProperties::SECURITY_LABEL, param.option.secOption.securityLabel); + properties.SetIntProp(KvDBProperties::SECURITY_FLAG, param.option.secOption.securityFlag); + } + DBCommon::SetDatabaseIds(properties, param.appId, param.userId, param.storeId); + return E_OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp b/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp new file mode 100755 index 000000000..d326f97ab --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/db_common.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 = remove(item.fileName.c_str()); + if (errCode != 0) { + LOGE("Remove file failed:%d", errno); + } + } + } + + void RemoveDirectories(const std::list &fileList, OS::FileType type) + { + for (const auto &item : fileList) { + if (item.fileType != type) { + continue; + } + int errCode = OS::RemoveDBDirectory(item.fileName); + if (errCode != 0) { + LOGE("Remove directory failed:%d", errno); + } + } + } +} + +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 hexChar = "0123456789ABCDEF"; + std::string outString; + for (auto &entry : inVec) { + outString.push_back(hexChar[entry >> 4]); // high 4 bit to one hex. + outString.push_back(hexChar[entry & 0x0F]); // low 4 bit 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 char *hex = "0123456789ABCDEF"; + 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] = hex[data[i] >> 4]; // high 4 bit to one hex. + buff[byteHexNum * i + 1] = hex[data[i] & 0x0F]; // low 4 bit 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 ""; + } + const char *hex = "0123456789abcdef"; + std::string tmp; + for (auto item : origStr) { + unsigned char currentByte = static_cast(item); + tmp.push_back(hex[currentByte >> 4]); // high 4 bit to one hex. + tmp.push_back(hex[currentByte & 0x0F]); // low 4 bit 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; + } + + 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) +{ + if (!isCreate) { + return E_OK; + } + if (directory.empty()) { + return -E_INVALID_ARGS; + } + + std::string newDir = directory; + if (newDir.back() != '/') { + newDir += "/"; + } + + newDir += identifierName; + 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; +} + +void DBCommon::SetDatabaseIds(KvDBProperties &properties, const std::string &appId, const std::string &userId, + const std::string &storeId) +{ + properties.SetStringProp(KvDBProperties::APP_ID, appId); + properties.SetStringProp(KvDBProperties::USER_ID, userId); + properties.SetStringProp(KvDBProperties::STORE_ID, 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); + properties.SetStringProp(KvDBProperties::IDENTIFIER_DATA, hashIdentifier); + std::string hashDir = TransferHashString(oriStoreDir); + std::string hexHashDir = TransferStringToHex(hashDir); + properties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, hexHashDir); +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/db_constant.cpp b/services/distributeddataservice/libs/distributeddb/common/src/db_constant.cpp new file mode 100755 index 000000000..827121d4d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/db_constant.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 "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"; +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/event_fd.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/event_fd.h new file mode 100755 index 000000000..7700f8678 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent.h new file mode 100755 index 000000000..ce66e008d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent_loop.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/include/ievent_loop.h new file mode 100755 index 000000000..b7451b8c4 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.cpp new file mode 100755 index 000000000..c33970d9b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.cpp @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + if (!events || (events & (~allEvents))) { + return false; + } + return true; +} + +bool EventImpl::IsValidArg(EventTime timeout) const +{ + if ((timeout < 0) || (timeout > MAX_TIME_VALUE)) { + return false; + } + return true; +} + +DEFINE_OBJECT_TAG_FACILITIES(EventImpl) +} + diff --git a/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_impl.h new file mode 100755 index 000000000..48964b558 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.cpp new file mode 100755 index 000000000..6519f3d5e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_epoll.h new file mode 100755 index 000000000..d802afad4 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.cpp new file mode 100755 index 000000000..826a65322 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.cpp @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +{ + auto it = polling_.find(event); + if (it != polling_.end()) { + return true; + } + return false; +} + +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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_impl.h new file mode 100755 index 000000000..91c270ba3 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.cpp new file mode 100755 index 000000000..21b0a7301 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.cpp @@ -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. + */ + +#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) +{ + 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) +{ + return E_OK; +} + +int EventLoopSelect::AddEvent(EventImpl *event) +{ + return E_OK; +} + +int EventLoopSelect::RemoveEvent(EventImpl *event) +{ + return E_OK; +} + +int EventLoopSelect::ModifyEvent(EventImpl *event, bool isAdd, EventsMask events) +{ + return E_OK; +} + +DEFINE_OBJECT_TAG_FACILITIES(EventLoopSelect) +} + +#endif diff --git a/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.h b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/event_loop_select.h new file mode 100755 index 000000000..79a6fbdd6 --- /dev/null +++ b/services/distributeddataservice/libs/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_; +}; +} +#endif // EVENT_LOOP_USE_SELECT +#endif // EVENT_LOOP_SELECT_H diff --git a/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent.cpp new file mode 100755 index 000000000..a033d06b9 --- /dev/null +++ b/services/distributeddataservice/libs/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; +} +} diff --git a/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent_loop.cpp b/services/distributeddataservice/libs/distributeddb/common/src/evloop/src/ievent_loop.cpp new file mode 100755 index 000000000..ce30ab360 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/flatbuffer_schema.cpp b/services/distributeddataservice/libs/distributeddb/common/src/flatbuffer_schema.cpp new file mode 100755 index 000000000..a9f97fe0e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/flatbuffer_schema.cpp @@ -0,0 +1,1023 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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" +#ifndef OMIT_FLATBUFFER +#include +#endif // OMIT_FLATBUFFER +#include +#include +#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 +} + +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() >= SCHEMA_STRING_SIZE_LIMIT * 2) { // 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; + } + // Try base64 decode. base64 decoded length less then original length. + outDecoded.resize(inOriginal.size()); + if (outDecoded.size() != inOriginal.size()) { // Unlikely in normal situation + // Do such check since we will directly access the memory inside outDecoded following + LOGE("[FBSchema][Is] Resize=%zu fail, OOM.", inOriginal.size()); + return false; + } + auto decodeBuf = reinterpret_cast(outDecoded.data()); // changeable buffer + int decodeLen = b64_pton(inOriginal.c_str(), decodeBuf, outDecoded.size()); + LOGD("[FBSchema][Is] Base64 decodeLen=%d, oriLen=%zu.", decodeLen, inOriginal.size()); + if (decodeLen <= 0 || decodeLen >= static_cast(inOriginal.size())) { + outDecoded.clear(); + return false; + } + // Base64 decode success! It should be an encode form of flatBuffer-Schema. + outDecoded.erase(outDecoded.begin() + decodeLen, outDecoded.end()); // Erase surplus space + auto decodeSchemaBuf = reinterpret_cast(outDecoded.c_str()); // Reget the memory after erase. + flatbuffers::Verifier decodeVerifier(decodeSchemaBuf, outDecoded.size()); + if (reflection::VerifySizePrefixedSchemaBuffer(decodeVerifier)) { + return true; + } + LOGE("[FBSchema][Is] Base64 decode success but verify fail."); + 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(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()) != SCHEMA_SUPPORT_VERSION) { + LOGE("[FBSchema][ParseRootAttr] Unexpect SCHEMA_VERSION=%s.", versionAttr->value()->c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + owner_.schemaVersion_ = SCHEMA_SUPPORT_VERSION; + description_ += (KEYWORD_SCHEMA_VERSION + "=" + SCHEMA_SUPPORT_VERSION + ";"); + + auto skipsizeAttr = rootTableAttr->LookupByKey(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 = std::atoi(skipsizeStr.c_str()); // std::stoi will throw exception + if (std::to_string(skipsizeInt) != skipsizeStr || skipsizeInt < 0 || + static_cast(skipsizeInt) > 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_ += (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 < SCHEMA_FEILD_PATH_DEPTH_MAX; depth++) { + if (owner_.schemaDefine_.count(depth) != 0) { + fieldPathCount += owner_.schemaDefine_[depth].size(); + } + } + if (fieldPathCount > 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() > 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(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() >= 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) +{ + if (indexStr.empty()) { + return true; + } + if (indexStr == std::string("0")) { // In fact, test found that attrValue will be "0" if not exist + return true; + } + return false; +} +} + +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() > 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) +{ + LOGW("FlatBuffer Omit From Compile."); + return false; +} + +int SchemaObject::FlatBufferSchema::ParseFlatBufferSchema(const std::string &inDecoded) +{ + owner_.schemaType_ = SchemaType::FLATBUFFER; // For fix compile warning + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::CompareFlatBufferDefine(const FlatBufferSchema &other) const +{ + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::VerifyFlatBufferValue(const RawValue &inValue, bool tryNoSizePrefix) const +{ + return -E_NOT_PERMIT; +} + +int SchemaObject::FlatBufferSchema::ExtractFlatBufferValue(RawString inPath, const RawValue &inValue, + TypeValue &outExtract, bool tryNoSizePrefix) const +{ + return -E_NOT_PERMIT; +} +#endif // OMIT_FLATBUFFER +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/hash.cpp b/services/distributeddataservice/libs/distributeddb/common/src/hash.cpp new file mode 100755 index 000000000..17e0cad33 --- /dev/null +++ b/services/distributeddataservice/libs/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); // 4 is offset + uint32_t x = (hash & 0xf0000000); + if (x != 0) { + hash ^= (x >> 24); // 24 is offset + } + hash &= ~x; + } + return (hash & 0x7fffffff); +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp b/services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp new file mode 100755 index 000000000..60a63efa2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/json_object.cpp @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} +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) +{ + auto begin = reinterpret_cast(inString.c_str()); + auto end = begin + inString.size(); + return CalculateNestDepth(begin, end); +} + +uint32_t JsonObject::CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd) +{ + if (dataBegin == nullptr || dataEnd == nullptr || dataBegin >= dataEnd) { + return -E_INVALID_ARGS; + } + 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; +} + +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; + } + uint32_t nestDepth = CalculateNestDepth(inString); + if (nestDepth > maxNestDepth_) { + LOGE("[Json][Parse] Json nest depth=%u exceed max allowed=%u.", nestDepth, maxNestDepth_); + return -E_JSON_PARSE_FAIL; + } + 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; + } + // 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; + } + uint32_t nestDepth = CalculateNestDepth(dataBegin, dataEnd); + if (nestDepth > maxNestDepth_) { + LOGE("[Json][Parse] Json nest depth=%u exceed max allowed=%u.", nestDepth, maxNestDepth_); + return -E_JSON_PARSE_FAIL; + } + 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; + } + // 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(); + } + 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_); +} + +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) ? true : false; +} + +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) { + LOGE("[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; +} + +// 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; + } +} +} + +int JsonObject::InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue) +{ + 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; + uint32_t nearDepth = 0; + int errCode = LocateJsonValueByFieldPath(inPath, exact, nearest, nearDepth); + if (errCode != -E_NOT_FOUND) { // Path already exist + 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]]); + } + // Here "nearest" points to the JsonValue(null-type now) corresponding to the last field + 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 + } + // Here, JsonValue exist at exact path, "nearest" is "exact" parent. + return E_OK; +} +#else // OMIT_JSON +uint32_t JsonObject::SetMaxNestDepth(uint32_t nestDepth) +{ + return 0; +} + +uint32_t JsonObject::CalculateNestDepth(const std::string &inString) +{ + return 0; +} + +uint32_t JsonObject::CalculateNestDepth(const uint8_t *dataBegin, const uint8_t *dataEnd) +{ + return 0; +} + +JsonObject::JsonObject(const JsonObject &other) = default; + +JsonObject& JsonObject::operator=(const JsonObject &other) = default; + +int JsonObject::Parse(const std::string &inString) +{ + LOGW("[Json][Parse] Json Omit From Compile."); + return -E_NOT_PERMIT; +} + +int JsonObject::Parse(const std::vector &inData) +{ + LOGW("[Json][Parse] Json Omit From Compile."); + return -E_NOT_PERMIT; +} + +int JsonObject::Parse(const uint8_t *dataBegin, const uint8_t *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 +{ + return false; +} + +int JsonObject::GetFieldTypeByFieldPath(const FieldPath &inPath, FieldType &outType) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetFieldValueByFieldPath(const FieldPath &inPath, FieldValue &outValue) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPath(const FieldPath &inPath, std::set &outSubPath) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPath(const std::set &inPath, std::set &outSubPath) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPathAndType(const FieldPath &inPath, std::map &outSubPathType) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetSubFieldPathAndType(const std::set &inPath, + std::map &outSubPathType) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetArraySize(const FieldPath &inPath, uint32_t &outSize) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::GetArrayContentOfStringOrStringArray(const FieldPath &inPath, + std::vector> &outContent) const +{ + return -E_NOT_PERMIT; +} + +int JsonObject::InsertField(const FieldPath &inPath, FieldType inType, const FieldValue &inValue) +{ + return -E_NOT_PERMIT; +} + +int JsonObject::DeleteField(const FieldPath &inPath) +{ + return -E_NOT_PERMIT; +} +#endif // OMIT_JSON +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.cpp b/services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.cpp new file mode 100755 index 000000000..dc707d71f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.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. + */ + +#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); +} +} diff --git a/services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.h b/services/distributeddataservice/libs/distributeddb/common/src/lock_status_observer.h new file mode 100755 index 000000000..ae221b5fa --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp b/services/distributeddataservice/libs/distributeddb/common/src/log_print.cpp new file mode 100755 index 000000000..b6491dfd3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/log_print.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. + */ + +#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, ...) +{ + 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/services/distributeddataservice/libs/distributeddb/common/src/notification_chain.cpp b/services/distributeddataservice/libs/distributeddb/common/src/notification_chain.cpp new file mode 100755 index 000000000..8db202781 --- /dev/null +++ b/services/distributeddataservice/libs/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 a 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/services/distributeddataservice/libs/distributeddb/common/src/param_check_utils.cpp b/services/distributeddataservice/libs/distributeddb/common/src/param_check_utils.cpp new file mode 100755 index 000000000..0086834b9 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/param_check_utils.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) { + LOGE("Failed to canonicalize the data dir."); + 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) +{ + if (!IsStoreIdSafe(storeId)) { + return false; + } + if (userId.empty() || userId.length() > DBConstant::MAX_USER_ID_LENGTH || + appId.empty() || appId.length() > DBConstant::MAX_APP_ID_LENGTH) { + LOGE("Invalid user or app info[%zu][%zu]", userId.length(), appId.length()); + return false; + } + + if ((userId.find(DBConstant::ID_CONNECTOR) != std::string::npos) || + (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; + } + if (conflictType != 0) { + return false; + } + return true; +} + +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, + 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 (!ParamCheckUtils::CheckDataDir(param.option.dataDir, canonicalDir)) { + LOGE("[AutoLaunch] CheckDataDir is invalid."); + return -E_INVALID_ARGS; + } + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/common/src/parcel.cpp b/services/distributeddataservice/libs/distributeddb/common/src/parcel.cpp new file mode 100755 index 000000000..08292ccd1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/parcel.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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::WriteInt(int32_t data) +{ + int32_t inData = HostToNet(data); + if (isError_ || parcelLen_ + sizeof(int32_t) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &inData, sizeof(int32_t)); + if (errCode != EOK) { + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(int32_t); + parcelLen_ += sizeof(int32_t); + return errCode; +} + +uint32_t Parcel::ReadInt(int32_t &val) +{ + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(int32_t) > totalLen_) { + isError_ = true; + return 0; + } + val = *(reinterpret_cast(bufPtr_)); + bufPtr_ += sizeof(int32_t); + parcelLen_ += sizeof(int32_t); + val = NetToHost(val); + return sizeof(int32_t); +} + +int Parcel::WriteUInt32(uint32_t data) +{ + uint32_t inData = HostToNet(data); + if (isError_ || parcelLen_ + sizeof(uint32_t) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &inData, sizeof(uint32_t)); + if (errCode != EOK) { + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(uint32_t); + parcelLen_ += sizeof(uint32_t); + return errCode; +} + +uint32_t Parcel::ReadUInt32(uint32_t &val) +{ + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(uint32_t) > totalLen_) { + isError_ = true; + return 0; + } + val = *(reinterpret_cast(bufPtr_)); + bufPtr_ += sizeof(uint32_t); + parcelLen_ += sizeof(uint32_t); + val = NetToHost(val); + return sizeof(uint32_t); +} + +int Parcel::WriteUInt64(uint64_t data) +{ + uint64_t inData = HostToNet(data); + if (isError_ || parcelLen_ + sizeof(uint64_t) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &inData, sizeof(uint64_t)); + if (errCode != EOK) { + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += sizeof(uint64_t); + parcelLen_ += sizeof(uint64_t); + return errCode; +} + +uint32_t Parcel::ReadUInt64(uint64_t &val) +{ + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(uint64_t) > totalLen_) { + isError_ = true; + return 0; + } + val = *(reinterpret_cast(bufPtr_)); + bufPtr_ += sizeof(uint64_t); + parcelLen_ += sizeof(uint64_t); + val = NetToHost(val); + return sizeof(uint64_t); +} + +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) { + isError_ = true; + return -E_PARSE_FAIL; + } + uint32_t len = inVal.size(); + uint64_t stepLen = sizeof(uint32_t) + static_cast(inVal.size()); + len = HostToNet(len); + if (isError_ || stepLen > INT32_MAX || parcelLen_ + BYTE_8_ALIGN(stepLen) > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + errno_t errCode = memcpy_s(bufPtr_, totalLen_ - parcelLen_, &len, sizeof(uint32_t)); + if (errCode != EOK) { + 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) { + isError_ = true; + return -E_SECUREC_ERROR; + } + bufPtr_ += inVal.size(); + bufPtr_ += BYTE_8_ALIGN(stepLen) - stepLen; + parcelLen_ += BYTE_8_ALIGN(stepLen); + return errCode; +} + +uint32_t Parcel::ReadString(std::string &outVal) +{ + if (isError_ || bufPtr_ == nullptr || parcelLen_ + sizeof(uint32_t) > totalLen_) { + 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_) { + 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); +} + +#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 = WriteMultiVerCommit(iter); + if (errCode != E_OK) { + 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: %llu", size); + return 0; + } + for (uint64_t i = 0; i < size; i++) { + MultiVerCommitNode commit; + len += ReadMultiVerCommit(commit); + 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) { + isError_ = true; + return -E_INVALID_ARGS; + } + if (isError_ || parcelLen_ + bufLen > totalLen_) { + isError_ = true; + return -E_PARSE_FAIL; + } + uint32_t leftLen = static_cast(totalLen_ - parcelLen_); + int errCode = memcpy_s(bufPtr_, leftLen, buffer, bufLen); + if (errCode != EOK) { + isError_ = true; + return -E_SECUREC_ERROR; + } + uint32_t length = (BYTE_8_ALIGN(bufLen) < leftLen) ? BYTE_8_ALIGN(bufLen) : leftLen; + bufPtr_ += length; + parcelLen_ += length; + return errCode; +} +uint32_t Parcel::ReadBlob(char *buffer, uint32_t bufLen) +{ + if (buffer == nullptr) { + isError_ = true; + return 0; + } + uint32_t leftLen = static_cast(totalLen_ - parcelLen_); + if (isError_ || parcelLen_ + bufLen > totalLen_) { + isError_ = true; + return 0; + } + int errCode = memcpy_s(buffer, bufLen, bufPtr_, bufLen); + if (errCode != EOK) { + 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::GetIntLen() +{ + return sizeof(int32_t); +} + +uint32_t Parcel::GetUInt32Len() +{ + return sizeof(uint32_t); +} + +uint32_t Parcel::GetUInt64Len() +{ + return sizeof(uint64_t); +} + +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/services/distributeddataservice/libs/distributeddb/common/src/performance_analysis.cpp b/services/distributeddataservice/libs/distributeddb/common/src/performance_analysis.cpp new file mode 100755 index 000000000..779644068 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/performance_analysis.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 (std::vector::iterator 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 +{ + if (stepNum_ >= MAX_TIMERECORD_STEP_NUM) { + return false; + } + + if (step < stepNum_) { + return true; + } + return false; +} + +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:%d, curTime:%lu", 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:%d, curTime:%lu", 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/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp b/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.cpp new file mode 100755 index 000000000..c56ba8f53 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/platform_specific.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 "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) +{ + if (access(filePath.c_str(), ACCESS_MODE_EXISTENCE) < 0) { + return false; + } + return true; +} + +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; + } + 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; + } + 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) { + LOGE("Get file[%zu] size failed, errno [%d].", fileUrl.size(), errno); + return -E_INVALID_ARGS; + } + + size = 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; + } + (void)memset_s(realPath, MAX_PATH_LENGTH + 1, 0, MAX_PATH_LENGTH + 1); + if (realpath(inOriPath.c_str(), realPath) == nullptr) { + LOGE("[RealPath] Get realpath for inOriPath fail:%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 = 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; +} +} // namespace OS +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/query.cpp b/services/distributeddataservice/libs/distributeddb/common/src/query.cpp new file mode 100755 index 000000000..462acd960 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/query.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 "query.h" +namespace DistributedDB { +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; +} + +void Query::ExecuteLogicOperation(QueryObjType operType) +{ + switch (operType) { + case QueryObjType::OR: + queryExpression_.Or(); + break; + case QueryObjType::AND: + queryExpression_.And(); + break; + default: + return; + } +} + +void Query::ExecuteOrderBy(const std::string &field, bool isAsc) +{ + queryExpression_.OrderBy(field, isAsc); +} + +void Query::ExecuteLimit(int number, int offset) +{ + queryExpression_.Limit(number, offset); +} + +void Query::ExecuteLike(const std::string &field, const std::string &value) +{ + queryExpression_.Like(field, value); +} + +void Query::ExecuteNotLike(const std::string &field, const std::string &value) +{ + queryExpression_.NotLike(field, value); +} + +void Query::ExecuteIsNull(const std::string &field) +{ + queryExpression_.IsNull(field); +} + +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; + } +} +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/common/src/query_expression.cpp b/services/distributeddataservice/libs/distributeddb/common/src/query_expression.cpp new file mode 100755 index 000000000..72aeeab76 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/query_expression.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + } + + if (isNeedFieldPath) { + FieldPath outPath; + if (SchemaUtils::ParseAndCheckFieldPath(field, outPath) != E_OK) { + SetErrFlag(false); + LOGE("Field path illegal!"); + return; + } + } + queryInfo_.emplace_back(QueryObjNode{queryOperType, field, type, values}); +} + +QueryExpression::QueryExpression() + : errFlag_(true) +{} + +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; +} + +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() +{ + return prefixKey_; +} + +std::string QueryExpression::GetSuggestIndex() +{ + return suggestIndex_; +} + +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::SetErrFlag(bool flag) +{ + errFlag_ = flag; +} + +bool QueryExpression::GetErrFlag() +{ + return errFlag_; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/ref_object.cpp b/services/distributeddataservice/libs/distributeddb/common/src/ref_object.cpp new file mode 100755 index 000000000..a3cd10760 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/runtime_context.cpp b/services/distributeddataservice/libs/distributeddb/common/src/runtime_context.cpp new file mode 100755 index 000000000..9acf1e19c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.cpp b/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.cpp new file mode 100755 index 000000000..445de2cc2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.cpp @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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) +{ +} + +// 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; +} + +// 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_); +} + +// 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, + KvStoreObserver *observer, int conflictType, KvStoreNbConflictNotifier conflictNotifier) +{ + return autoLaunch_.EnableKvStoreAutoLaunch(properties, notifier, observer, conflictType, conflictNotifier); +} + +int RuntimeContextImpl::DisableKvStoreAutoLaunch(const std::string &identifier) +{ + return autoLaunch_.DisableKvStoreAutoLaunch(identifier); +} + +void RuntimeContextImpl::GetAutoLaunchSyncDevices(const std::string &identifier, + std::vector &devices) const +{ + return autoLaunch_.GetAutoLaunchSyncDevices(identifier, devices); +} + +void RuntimeContextImpl::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) +{ + autoLaunch_.SetAutoLaunchRequestCallback(callback); +} + +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_); + if (systemApiAdapter_ == nullptr) { + return false; + } + return true; +} + +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; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.h b/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.h new file mode 100755 index 000000000..43f077785 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/runtime_context_impl.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 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" + +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; + + // 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, + KvStoreObserver *observer, int conflictType, KvStoreNbConflictNotifier conflictNotifier) override; + + int DisableKvStoreAutoLaunch(const std::string &identifier) override; + + void GetAutoLaunchSyncDevices(const std::string &identifier, std::vector &devices) const override; + + void SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) override; + + NotificationChain::Listener *RegisterLockStatusLister(const LockStatusNotifier &action, int &errorCode) 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; + +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::mutex systemApiAdapterLock_; + std::shared_ptr systemApiAdapter_; + mutable std::mutex lockStatusLock_; // Mutex for lockStatusObserver_. + LockStatusObserver *lockStatusObserver_; +}; +} // namespace DistributedDB + +#endif // RUNTIME_CONTEXT_IMPL_H diff --git a/services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp b/services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp new file mode 100755 index 000000000..3029bd4d4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/schema_object.cpp @@ -0,0 +1,1220 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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" + +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) +{ + 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 += "(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; +} + +namespace { +inline SchemaType ReadSchemaType(uint8_t inType) +{ + if (inType >= static_cast(SchemaType::UNRECOGNIZED)) { + return SchemaType::UNRECOGNIZED; + } + return static_cast(inType); +} +} + +// 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 SchemaObject::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=%u 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 SchemaObject::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; +} + +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; +} + +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() > 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=%zu invalid, skipsize=%u.", inValue.second, schemaSkipSize_); + return -E_FLATBUFFER_VERIFY_FAIL; + } + + RawValue rawValue; + std::vector cache; + if (schemaSkipSize_ % 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_ % 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{KEYWORD_SCHEMA_INDEXES}) != 0) { + indexMetaFieldCount++; + FieldType type = metaFieldPathType.at(FieldPath{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{KEYWORD_SCHEMA_SKIPSIZE}) != 0) { + skipSizeMetaFieldCount++; + FieldType type = metaFieldPathType.at(FieldPath{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() != (SCHEMA_META_FEILD_COUNT_MIN + indexMetaFieldCount + skipSizeMetaFieldCount)) { + LOGE("[Schema][CheckMeta] Unrecognized metaField exist: total=%u, indexField=%u, skipSizeField=%u.", + 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() < SCHEMA_META_FEILD_COUNT_MIN || + metaFieldPathType.size() > 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{KEYWORD_SCHEMA_VERSION}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_VERSION but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + FieldType type = metaFieldPathType.at(FieldPath{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{KEYWORD_SCHEMA_MODE}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_MODE but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + type = metaFieldPathType.at(FieldPath{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{KEYWORD_SCHEMA_DEFINE}) == 0) { + LOGE("[Schema][CheckMeta] Expect metafield SCHEMA_DEFINE but not find."); + return -E_SCHEMA_PARSE_FAIL; + } + type = metaFieldPathType.at(FieldPath{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{KEYWORD_SCHEMA_VERSION}, versionValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + if (SchemaUtils::Strip(versionValue.stringValue) != SCHEMA_SUPPORT_VERSION) { + LOGE("[Schema][ParseVerMode] Unexpected SCHEMA_VERSION=%s.", versionValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaVersion_ = 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{KEYWORD_SCHEMA_MODE}, modeValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + std::string modeStripped = SchemaUtils::Strip(modeValue.stringValue); + if (modeStripped != KEYWORD_MODE_STRICT && modeStripped != KEYWORD_MODE_COMPATIBLE) { + LOGE("[Schema][ParseVerMode] Unexpected SCHEMA_MODE=%s.", modeValue.stringValue.c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + schemaMode_ = ((modeStripped == 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{KEYWORD_SCHEMA_DEFINE}}; + uint32_t fieldNameCount = 0; + for (uint32_t depth = 0; depth < 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 == 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 > 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{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{KEYWORD_SCHEMA_INDEXES}, oriIndexArray); + if (errCode != E_OK) { + LOGE("[Schema][ParseIndex] GetArrayContent Fail, errCode=%d.", errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (oriIndexArray.size() > 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{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{KEYWORD_SCHEMA_SKIPSIZE}, skipSizeValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + if (skipSizeValue.integerValue < 0 || static_cast(skipSizeValue.integerValue) > 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() > 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 < 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=%u less than oldSize=%u at depth=%u.", 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=%u more than oldSize=%u at depth=%u 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=%lld mismatch newDefault=%lld.", 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 < 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/services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp b/services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp new file mode 100755 index 000000000..8f90faa88 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/schema_utils.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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" + +namespace DistributedDB { +namespace +{ + // Currently supported types + const std::map FIELD_TYPE_DIC = { + {KEYWORD_TYPE_BOOL, FieldType::LEAF_FIELD_BOOL}, + {KEYWORD_TYPE_INTEGER, FieldType::LEAF_FIELD_INTEGER}, + {KEYWORD_TYPE_LONG, FieldType::LEAF_FIELD_LONG}, + {KEYWORD_TYPE_DOUBLE, FieldType::LEAF_FIELD_DOUBLE}, + {KEYWORD_TYPE_STRING, FieldType::LEAF_FIELD_STRING}, + }; + + 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, KEYWORD_ATTR_NOT_NULL.size(), KEYWORD_ATTR_NOT_NULL) == 0) { + pos = pos + KEYWORD_ATTR_NOT_NULL.size() - 1; + return COLUMN_NOT_NULL; + } else if (oriContent.compare(pos, KEYWORD_ATTR_DEFAULT.size(), KEYWORD_ATTR_DEFAULT) == 0) { + pos = pos + 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 == 1) { // state 1 :Indicates that only type information is currently available + outAttrString[0].push_back(inAttrString[i]); + } else if (state == 3) { // state 3 :Gets the NOT_NULL keyword + outAttrString[1] = KEYWORD_ATTR_NOT_NULL; + } else if (state == 7) { // state 7 :Contains complete information + // Get default string. Now transfer matrix can ensure > 1, but you should pay attention when fix it + outAttrString[2] = inAttrString.substr(i - 1); + break; + } else if (state < 0) { + LOGD("Split Schema Attribute err, err state [%d]", state); + return -E_SCHEMA_PARSE_FAIL; + } + } + // 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(KEYWORD_ATTR_VALUE_TRUE) == 0) { + outAttr.defaultValue.boolValue = true; + return E_OK; + } else if (defaultContent.compare(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() > 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 = std::atoi(defaultContent.c_str()); + 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 = std::atoll(defaultContent.c_str()); + 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(KEYWORD_ATTR_VALUE_NULL) == 0 && outAttr.hasNotNullConstraint) { + LOGE("NOT NULL and DEFAULT null Simultaneously"); + return -E_SCHEMA_PARSE_FAIL; + } else if (defaultContent.compare(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]", outAttr.type, errCode); + return errCode; +} + +int SchemaUtils::ParseAndCheckSchemaAttribute(const std::string &inAttrString, SchemaAttribute &outAttr) +{ + 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); + if (errCode != E_OK) { + LOGD("Grammatical error, please check!"); + return errCode; + } + + return E_OK; +} + +int SchemaUtils::ParseSchemaAttribute(std::vector &attrContext, SchemaAttribute &outAttr) +{ + // 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 (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]); + } + + 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) +{ + std::string tempInPathString = inPathString; + TrimFiled(tempInPathString); + bool hasPrefix = false; + int errCode = CheckDollarDotPrefix(tempInPathString, hasPrefix); + if (errCode != E_OK) { + LOGE("CheckDollarDotPrefix Fail."); + return errCode; + } + 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() > 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() > 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::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/services/distributeddataservice/libs/distributeddb/common/src/semaphore.cpp b/services/distributeddataservice/libs/distributeddb/common/src/semaphore.cpp new file mode 100755 index 000000000..f2ef2096a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/semaphore.cpp @@ -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. + */ + +#include "semaphore.h" + +#include + +namespace DistributedDB { +using std::unique_lock; +using std::mutex; +using std::condition_variable; + +Semaphore::Semaphore(int count) + : count_(count) +{} + +Semaphore::~Semaphore() +{} + +bool Semaphore::WaitSemaphore(int waitSecond) +{ + unique_lock lock(lockMutex_); + bool result = cv_.wait_for(lock, std::chrono::seconds(waitSecond), + std::bind(&Semaphore::CompareCount, this)); + if (result == true) { + --count_; + } + return result; +} + +void Semaphore::WaitSemaphore() +{ + unique_lock lock(lockMutex_); + cv_.wait(lock, std::bind(&Semaphore::CompareCount, this)); + --count_; +} + +void Semaphore::SendSemaphore() +{ + unique_lock lock(lockMutex_); + count_++; + cv_.notify_one(); +} + +bool Semaphore::CompareCount() const +{ + return count_ > 0; +} +} diff --git a/services/distributeddataservice/libs/distributeddb/common/src/task_pool.cpp b/services/distributeddataservice/libs/distributeddb/common/src/task_pool.cpp new file mode 100755 index 000000000..2cdeb5d34 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.cpp b/services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.cpp new file mode 100755 index 000000000..4c0ce9a0f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.cpp @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#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 { + // No task exist, force release memory cache for this thread + (void)mallopt(M_PURGE, 0); + 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/services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.h b/services/distributeddataservice/libs/distributeddb/common/src/task_pool_impl.h new file mode 100644 index 000000000..7e533a2d6 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/task_queue.cpp b/services/distributeddataservice/libs/distributeddb/common/src/task_queue.cpp new file mode 100755 index 000000000..4825fb831 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/task_queue.cpp @@ -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. + */ + +#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; + } + } + if (!tasks_.empty()) { + return false; + } + return true; +} +} diff --git a/services/distributeddataservice/libs/distributeddb/common/src/task_queue.h b/services/distributeddataservice/libs/distributeddb/common/src/task_queue.h new file mode 100755 index 000000000..ea8337b7f --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.cpp b/services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.cpp new file mode 100755 index 000000000..ff8df05c2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 = monotonicTime - lastMonotonicTime_; + int64_t systemOffset = 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); + timeChangedNotifier_->NotifyEvent(TIME_CHANGE_EVENT, &changedOffset); + } + 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/services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.h b/services/distributeddataservice/libs/distributeddb/common/src/time_tick_monitor.h new file mode 100755 index 000000000..64d020048 --- /dev/null +++ b/services/distributeddataservice/libs/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 timeChangeOffset) 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/services/distributeddataservice/libs/distributeddb/common/src/types_export.cpp b/services/distributeddataservice/libs/distributeddb/common/src/types_export.cpp new file mode 100755 index 000000000..5a4ee25ea --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/common/src/value_object.cpp b/services/distributeddataservice/libs/distributeddb/common/src/value_object.cpp new file mode 100755 index 000000000..def2c810b --- /dev/null +++ b/services/distributeddataservice/libs/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) ? true : false); + return errCode; +} + +int ValueObject::Parse(const std::vector &inData) +{ + if (isValid_) { + return -E_NOT_PERMIT; + } + int errCode = value_.Parse(inData); + isValid_ = ((errCode == E_OK) ? true : false); + 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=%lld, offset=%u", + 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/services/distributeddataservice/libs/distributeddb/communicator/include/combine_status.h b/services/distributeddataservice/libs/distributeddb/communicator/include/combine_status.h new file mode 100644 index 000000000..017243e3a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/combine_status.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 COMBINE_STATUS_H +#define COMBINE_STATUS_H + +#include +#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/services/distributeddataservice/libs/distributeddb/communicator/include/communicator_aggregator.h b/services/distributeddataservice/libs/distributeddb/communicator/include/communicator_aggregator.h new file mode 100644 index 000000000..c91cd42b6 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/communicator_aggregator.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + int GetLocalIdentity(std::string &outTarget) const; + // 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); + +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); + 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); + + 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); + int OnAppLayerFrameReceive(const std::string &srcTarget, SerialBuffer *&inFrameBuffer, const ParseResult &inResult); + // 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/services/distributeddataservice/libs/distributeddb/communicator/include/communicator_type_define.h b/services/distributeddataservice/libs/distributeddb/communicator/include/communicator_type_define.h new file mode 100755 index 000000000..af35a2962 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/include/frame_combiner.h b/services/distributeddataservice/libs/distributeddb/communicator/include/frame_combiner.h new file mode 100755 index 000000000..b2a2bd049 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/frame_combiner.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 FRAME_COMBINER_H +#define FRAME_COMBINER_H + +#include +#include +#include +#include "semaphore.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; + Semaphore timerRemovedIndicator_{0}; + uint64_t incProgressId_ = 0; + uint64_t totalSizeByByte_ = 0; + std::map> combineWorkPool_; +}; +} // namespace DistributedDB + +#endif // FRAME_COMBINER_H diff --git a/services/distributeddataservice/libs/distributeddb/communicator/include/frame_retainer.h b/services/distributeddataservice/libs/distributeddb/communicator/include/frame_retainer.h new file mode 100755 index 000000000..5ea246874 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/frame_retainer.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 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/services/distributeddataservice/libs/distributeddb/communicator/include/iadapter.h b/services/distributeddataservice/libs/distributeddb/communicator/include/iadapter.h new file mode 100644 index 000000000..4bdab0b76 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/iadapter.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 IADAPTER_H +#define IADAPTER_H + +#include +#include +#include +#include "communicator_type_define.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; + + // 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 ~IAdapter() {}; +}; +} // namespace DistributedDB + +#endif // IADAPTER_H diff --git a/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator.h b/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator.h new file mode 100644 index 000000000..30060655b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator.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 ICOMMUNICATOR_H +#define ICOMMUNICATOR_H + +#include +#include +#include "message.h" +#include "ref_object.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +// inMsg is heap memory, its ownership transfers by calling OnMessageCallback +using OnMessageCallback = std::function; + +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; + // 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, bool nonBlock, uint32_t timeout) = 0; + virtual int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout, + 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/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator_aggregator.h b/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator_aggregator.h new file mode 100644 index 000000000..26c5a08de --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/icommunicator_aggregator.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 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 ~ICommunicatorAggregator() {}; +}; +} // namespace DistributedDB + +#endif // ICOMMUNICATORAGGREGATOR_H diff --git a/services/distributeddataservice/libs/distributeddb/communicator/include/message.h b/services/distributeddataservice/libs/distributeddb/communicator/include/message.h new file mode 100755 index 000000000..791c55e8f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/message.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + + 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_; + } +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/services/distributeddataservice/libs/distributeddb/communicator/include/message_transform.h b/services/distributeddataservice/libs/distributeddb/communicator/include/message_transform.h new file mode 100755 index 000000000..59b450380 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/include/network_adapter.h b/services/distributeddataservice/libs/distributeddb/communicator/include/network_adapter.h new file mode 100644 index 000000000..1116bf74d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/network_adapter.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 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(); + 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; + 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; +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_; + bool isLocalIdentityValid_ = false; + 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/services/distributeddataservice/libs/distributeddb/communicator/include/object_holder.h b/services/distributeddataservice/libs/distributeddb/communicator/include/object_holder.h new file mode 100644 index 000000000..348eab4b5 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/include/object_holder_typed.h b/services/distributeddataservice/libs/distributeddb/communicator/include/object_holder_typed.h new file mode 100644 index 000000000..abb8dad9c --- /dev/null +++ b/services/distributeddataservice/libs/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 + 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/services/distributeddataservice/libs/distributeddb/communicator/include/parse_result.h b/services/distributeddataservice/libs/distributeddb/communicator/include/parse_result.h new file mode 100755 index 000000000..e6b1db26c --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/parse_result.h @@ -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. + */ + +#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/services/distributeddataservice/libs/distributeddb/communicator/include/send_task_scheduler.h b/services/distributeddataservice/libs/distributeddb/communicator/include/send_task_scheduler.h new file mode 100755 index 000000000..a4be7923e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/include/send_task_scheduler.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/services/distributeddataservice/libs/distributeddb/communicator/src/combine_status.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/combine_status.cpp new file mode 100755 index 000000000..00eb86afc --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/combine_status.cpp @@ -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. + */ + +#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 +{ + if (combinedFragmentNo_.size() < fragmentCount_) { + return false; + } + return true; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.cpp new file mode 100755 index 000000000..394c6621f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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=%s, 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); +} + +int Communicator::SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout) +{ + return SendMessage(dstTarget, inMsg, nonBlock, timeout, nullptr); +} + +int Communicator::SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout, + const OnSendEnd &onEnd) +{ + if (dstTarget.size() == 0 || inMsg == nullptr) { + return -E_INVALID_ARGS; + } + + int error = E_OK; + // if error is not E_OK , null pointer will be returned + SerialBuffer *buffer = ProtocolProto::ToSerialBuffer(inMsg, error); + 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 config{nonBlock, timeout, inMsg->GetPriority()}; + errCode = commAggrHandle_->CreateSendTask(dstTarget, buffer, FrameType::APPLICATION_MESSAGE, config, 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/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.h b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.h new file mode 100644 index 000000000..e4668978f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator.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 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; + 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, bool nonBlock, uint32_t timeout) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout, + 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/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_aggregator.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_aggregator.cpp new file mode 100755 index 000000000..9bc59ee4e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_aggregator.cpp @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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=%s.", 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=%s.", 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(); +} + +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=%s.", 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> 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 + eachPacket.push_back(taskToSend.buffer->GetReadOnlyBytesForEntireBuffer()); + } else { + for (auto &entry : piecePackets) { + eachPacket.push_back(std::make_pair(&(entry[0]), entry.size())); + } + } + + 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}, length=%u.", inTask.dstTarget.c_str(), + entry.second); + ProtocolProto::DisplayPacketInformation(entry.first, entry.second); // For debug, delete in the future + errCode = adapterHandle_->SendBytes(inTask.dstTarget, entry.first, entry.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) +{ + 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); + } 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); + 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."); + } + } + // For communicator level target change + std::set relatedLabels; + 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 (relatedLabels.count(entry.first) != 0 && entry.second.second) { + 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) +{ + 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); + 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) +{ + 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); +} + +// 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) +{ + 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); + 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), + [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); + localSourceId_ = identityHash; + LOGI("[CommAggr][GenSrcId] identity=%s{private}, localSourceId=%llu.", identity.c_str(), ULL(identityHash)); +} + +bool CommunicatorAggregator::ReGenerateLocalSourceIdIfNeed() +{ + // If localSourceId is zero, pre-generate must have used an empty identity, re-fetch the identity and generate. + // The localSourceId is std::atomic, so there is no concurrency risk, no need lockguard here. + if (localSourceId_ == 0) { + GenerateLocalSourceId(); + return (localSourceId_ != 0); + } + return true; +} + +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; +} + +DEFINE_OBJECT_TAG_FACILITIES(CommunicatorAggregator) +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.cpp new file mode 100755 index 000000000..b3461bb77 --- /dev/null +++ b/services/distributeddataservice/libs/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 error = E_OK; + SerialBuffer *buffer = ProtocolProto::BuildLabelExchangeAck(localDistinctValue_, inSequenceId, errno); + if (error != E_OK) { + LOGE("[Linker][TriggerAck] BuildAck fail, error=%d", error); + return error; + } + // 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) +} diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.h b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.h new file mode 100755 index 000000000..58065f40a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/communicator_linker.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/services/distributeddataservice/libs/distributeddb/communicator/src/frame_combiner.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/frame_combiner.cpp new file mode 100755 index 000000000..44a7c8ab4 --- /dev/null +++ b/services/distributeddataservice/libs/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=%llu, frameId=%u.", ULL(sourceId), ULL(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=%llu, frameId=%u.", ULL(sourceId), ULL(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/services/distributeddataservice/libs/distributeddb/communicator/src/frame_header.h b/services/distributeddataservice/libs/distributeddb/communicator/src/frame_header.h new file mode 100644 index 000000000..c159e47ba --- /dev/null +++ b/services/distributeddataservice/libs/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; // Magic code to discern byte stream + uint16_t version; // Version to differentiate fields layout + uint32_t packetLen; // Length of total packet, include CommHeader and Padding + uint64_t checkSum; // Check sum of data that follows CommPhyHeader + uint64_t sourceId; // Indicate where this packet from + uint32_t frameId; // FrameId to identify frame + uint8_t packetType; // Some bits works individually, the high four bits indicates frameType + uint8_t paddingLen; // Unit byte, range from 0 to 7. + uint16_t dbIntVer; // 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; // Indicate length of frame before fragmentation. Frame include CommHeader no padding + uint16_t fragCount; // Indicate how many fragments this frame is divided into + uint16_t fragNo; // 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; // Version to differentiate fields layout + uint16_t reserved; // Reserved for future usage + uint32_t payLoadLen; // Indicate length of data that follows CommDivergeHeader + uint8_t commLabel[COMM_LABEL_LENGTH]; // Indicate which communicator to hand out this frame +}; + +/* + * MessageHeader used to describe a message + */ +struct MessageHeader { + uint16_t version; // Version to differentiate fields layout + uint16_t messageType; // Distinguish request/response/notify + uint32_t messageId; // Indicate message command + uint32_t sessionId; // For matching request and response + uint32_t sequenceId; // Sequence of message + uint32_t errorNo; // Indicate no error when zero + uint32_t dataLen; // Indicate length of data that follows MessageHeader +}; +} // namespace DistributedDB + +#endif // FRAMEHEADER_H diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/frame_retainer.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/frame_retainer.cpp new file mode 100755 index 000000000..e8d6db64e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.cpp new file mode 100755 index 000000000..4b8fe88eb --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.h b/services/distributeddataservice/libs/distributeddb/communicator/src/header_converter.h new file mode 100644 index 000000000..7cccfde4c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/src/message_transform.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/message_transform.cpp new file mode 100755 index 000000000..83da56519 --- /dev/null +++ b/services/distributeddataservice/libs/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); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/network_adapter.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/network_adapter.cpp new file mode 100755 index 000000000..b521f9a56 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/network_adapter.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_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"; +constexpr uint32_t MIN_MTU_SIZE = 1024; // 1KB +constexpr uint32_t MAX_MTU_SIZE = 5242880; // 5MB +} + +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 < MIN_MTU_SIZE) { + return MIN_MTU_SIZE; + } else if (inMtuSize > MAX_MTU_SIZE) { + return MAX_MTU_SIZE; + } else { + return (inMtuSize - (inMtuSize % sizeof(uint64_t))); // Octet alignment + } +} +} + +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) +{ + 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]; +} + +int NetworkAdapter::GetLocalIdentity(std::string &outTarget) +{ + std::lock_guard identityLockGuard(identityMutex_); + if (!isLocalIdentityValid_) { + DeviceInfos devInfo = processCommunicator_->GetLocalDeviceInfos(); + LOGI("[NAdapt][GetLocal] localIdentity=%s{private}.", devInfo.identifier.c_str()); + if (devInfo.identifier.empty()) { + return -E_PERIPHERAL_INTERFACE_FAIL; + } + localIdentity_ = devInfo.identifier; + isLocalIdentityValid_ = true; + } + 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=%d", 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; + } + LOGI("[NAdapt][OnDataRecv] Enter, from=%s{private}, length=%u", srcDevInfo.identifier.c_str(), length); + { + std::lock_guard onReceiveLockGard(onReceiveMutex_); + if (!onReceiveHandle_) { + LOGE("[NAdapt][OnDataRecv] onReceiveHandle invalid."); + return; + } + onReceiveHandle_(srcDevInfo.identifier, data, length); + } + // 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(); + } + } + } +} +} diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.cpp new file mode 100755 index 000000000..df5d93fc8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.cpp @@ -0,0 +1,992 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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; +const uint16_t DB_GLOBAL_VERSION = 2; // Compatibility Final Method. 2 Correspond To Version 1.1.3(103) +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 0x0F 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, 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; + } + } + + SerialBuffer *buffer = new (std::nothrow) SerialBuffer(); + if (buffer == nullptr) { + outErrorNo = -E_OUT_OF_MEMORY; + return nullptr; + } + // serializeLen maybe not 8-bytes aligned, let SerialBuffer deal with the padding. + uint32_t payLoadLength = serializeLen + sizeof(MessageHeader); + int errCode = buffer->AllocBufferByPayloadLength(payLoadLength, GetAppLayerFrameHeaderLength()); + if (errCode != E_OK) { + LOGE("[Proto][ToSerial] Alloc Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + + // Serialize the MessageHeader and data if need + errCode = SerializeMessage(buffer, inMsg); + if (errCode != E_OK) { + LOGE("[Proto][ToSerial] Serialize Fail, errCode=%d.", errCode); + outErrorNo = errCode; + delete buffer; + buffer = nullptr; + return nullptr; + } + outErrorNo = E_OK; + return buffer; +} + +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) +{ + SerialBuffer *buffer = ToSerialBuffer(inMsg, outErrorNo, 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> &outPieces) +{ + auto bufferBytesLen = inBuff->GetReadOnlyBytesForEntireBuffer(); + if (bufferBytesLen.second <= inMtuSize) { + return E_OK; + } + // 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 = inMtuSize - 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); + int errCode = FrameFragmentation(frameBytesLen.first + sizeof(CommPhyHeader), lengthToSplit, + fragCount, *oriPhyHeader, outPieces); + return errCode; +} + +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 < 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; +} + +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; +} + +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.packetLen = packetLen; + phyHeader.checkSum = 0; // Sum is calculated afterwards + phyHeader.sourceId = inInfo.sourceId; + phyHeader.frameId = inInfo.frameId; + phyHeader.packetType = packetType; + phyHeader.paddingLen = paddingLen; + phyHeader.dbIntVer = DB_GLOBAL_VERSION; + 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=%u.", 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) +{ + if (version != MSG_VERSION_BASE && version != MSG_VERSION_EXT) { + return false; + } + return true; +} + +bool ProtocolProto::IsFeedbackErrorMessage(uint32_t errorNo) +{ + if (errorNo == E_FEEDBACK_UNKNOWN_MESSAGE || errorNo == E_FEEDBACK_COMMUNICATOR_NOT_FOUND) { + return true; + } + return false; +} + +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.", phyHeader.packetLen); + 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=%u 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, uint32_t splitLength, uint16_t fragCount, + const CommPhyHeader &framePhyHeader, std::vector> &outPieces) +{ + // It can be guaranteed that fragCount >= 2 and also won't be too large + outPieces.resize(fragCount); // Note: should use resize other than reserve + uint32_t quotient = splitLength / fragCount; + uint16_t remainder = splitLength % fragCount; + uint16_t fragNo = 0; // Fragment index start from 0 + uint32_t byteOffset = 0; + + for (auto &entry : outPieces) { + uint32_t pieceFragLen = (fragNo != fragCount - 1) ? quotient : (quotient + remainder); // subtract 1 for index + uint32_t alignedPieceFragLen = BYTE_8_ALIGN(pieceFragLen); // Add padding length + uint32_t pieceTotalLen = alignedPieceFragLen + sizeof(CommPhyHeader) + sizeof(CommPhyOptHeader); + + // Since exception is disabled, we have to check the vector size to assure that memory is truly allocated + entry.resize(pieceTotalLen); // Note: should use resize other than reserve + if (entry.size() != pieceTotalLen) { + LOGE("[Proto][FrameFrag] Resize failed for length=%u", pieceTotalLen); + return -E_OUT_OF_MEMORY; + } + + CommPhyHeader pktPhyHeader; + HeaderConverter::ConvertNetToHost(framePhyHeader, pktPhyHeader); // Restore to host endian + pktPhyHeader.packetLen = pieceTotalLen; + pktPhyHeader.checkSum = 0; // The sum value need to be recalculate + pktPhyHeader.packetType |= PACKET_TYPE_FRAGMENTED; // Set the FragmentedFlag bit + pktPhyHeader.paddingLen = alignedPieceFragLen - pieceFragLen; // The former is always larger than latter + HeaderConverter::ConvertHostToNet(pktPhyHeader, pktPhyHeader); + + CommPhyOptHeader pktPhyOptHeader; + pktPhyOptHeader.frameLen = splitLength + sizeof(CommPhyHeader); + pktPhyOptHeader.fragCount = fragCount; + pktPhyOptHeader.fragNo = fragNo; + HeaderConverter::ConvertHostToNet(pktPhyOptHeader, pktPhyOptHeader); + + int errCode = FillFragmentPacket(pktPhyHeader, pktPhyOptHeader, splitStartBytes + byteOffset, + pieceFragLen, entry); + if (errCode != E_OK) { + LOGE("[Proto][FrameFrag] Fill packet fail, fragCount=%u, fragNo=%u", fragCount, fragNo); + return errCode; + } + + fragNo++; + byteOffset += pieceFragLen; + } + + 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, std::vector &outPacket) +{ + if (outPacket.empty()) { + return -E_INVALID_ARGS; + } + uint8_t *ptrPacket = &(outPacket[0]); + uint32_t leftLength = outPacket.size(); + + // 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[0]) + LENGTH_BEFORE_SUM_RANGE, + outPacket.size() - LENGTH_BEFORE_SUM_RANGE, sumResult); + if (errCode != E_OK) { + return -E_SUM_CALCULATE_FAIL; + } + auto ptrPhyHeader = reinterpret_cast(&(outPacket[0])); + ptrPhyHeader->checkSum = HostToNet(sumResult); + + return E_OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.h b/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.h new file mode 100644 index 000000000..2e28b8c1f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/protocol_proto.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 PROTOCOLPROTO_H +#define PROTOCOLPROTO_H + +#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" + +namespace DistributedDB { +struct PhyHeaderInfo { + uint64_t sourceId; + uint32_t frameId; + FrameType frameType; +}; + +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, 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> &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); + + // 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, uint32_t splitLength, uint16_t fragCount, + const CommPhyHeader &framePhyHeader, std::vector> &outPieces); + static int FillFragmentPacket(const CommPhyHeader &phyHeader, const CommPhyOptHeader &phyOptHeader, + const uint8_t *fragBytes, uint32_t fragLen, std::vector &outPacket); + + static std::map msgIdMapFunc_; +}; +} // namespace DistributedDB + +#endif // PROTOCOLPROTO_H diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/send_task_scheduler.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/send_task_scheduler.cpp new file mode 100755 index 000000000..25a60561e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.cpp b/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.cpp new file mode 100755 index 000000000..92cce786a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.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. + */ + +#include "serial_buffer.h" +#include +#include "securec.h" +#include "db_errno.h" +#include "communicator_type_define.h" + +namespace DistributedDB { +SerialBuffer::~SerialBuffer() +{ + if (!isExternalStackMemory_ && bytes_ != nullptr) { + delete[] bytes_; + } + bytes_ = nullptr; + externalBytes_ = nullptr; +} + +// In case buffer be directly send out, so padding is needed +int SerialBuffer::AllocBufferByPayloadLength(uint32_t inPayloadLen, uint32_t inHeaderLen) +{ + if (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; + } + bytes_ = new (std::nothrow) uint8_t[totalLen_]; + if (bytes_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + 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; + } + 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->externalBytes_ = externalBytes_; + twinBuffer->totalLen_ = totalLen_; + twinBuffer->headerLen_ = headerLen_; + twinBuffer->payloadLen_ = payloadLen_; + twinBuffer->paddingLen_ = paddingLen_; + twinBuffer->isExternalStackMemory_ = isExternalStackMemory_; + 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; + return E_OK; +} + +uint32_t SerialBuffer::GetSize() const +{ + if (bytes_ == nullptr && externalBytes_ == nullptr) { + return 0; + } + return totalLen_; +} + +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); + } +} +} diff --git a/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.h b/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.h new file mode 100644 index 000000000..135164744 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/communicator/src/serial_buffer.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 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); + + // 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; + + 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; +private: + uint8_t *bytes_ = nullptr; + const uint8_t *externalBytes_ = nullptr; + uint32_t totalLen_ = 0; + uint32_t headerLen_ = 0; + uint32_t payloadLen_ = 0; + uint32_t paddingLen_ = 0; + bool isExternalStackMemory_ = false; +}; +} // namespace DistributedDB + +#endif // SERIALBUFFER_H diff --git a/services/distributeddataservice/libs/distributeddb/include/auto_launch_export.h b/services/distributeddataservice/libs/distributeddb/include/auto_launch_export.h new file mode 100755 index 000000000..2c9e1f1b7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/include/auto_launch_export.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 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" + +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; +}; + +struct AutoLaunchParam { + std::string userId; + std::string appId; + std::string storeId; + AutoLaunchOption option; + AutoLaunchNotifier notifier; +}; + +using AutoLaunchRequestCallback = std::function; +} // namespace DistributedDB + +#endif // DISTRIBUTEDDB_AUTO_LAUNCH_EXPORT_H diff --git a/services/distributeddataservice/libs/distributeddb/include/query.h b/services/distributeddataservice/libs/distributeddb/include/query.h new file mode 100755 index 000000000..e8b90b283 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/include/query.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "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() + { + Query query; + return query; + } + + 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) + { + ExecuteOrderBy(field, isAsc); + return *this; + } + + DB_API Query &Limit(int number, int offset = 0) + { + ExecuteLimit(number, offset); + return *this; + } + + DB_API Query &Like(const std::string &field, const std::string &value) + { + ExecuteLike(field, value); + return *this; + } + + DB_API Query &NotLike(const std::string &field, const std::string &value) + { + ExecuteNotLike(field, value); + return *this; + } + + template + DB_API Query &In(const std::string &field, const std::vector &values) + { + std::vector fieldValues; + QueryValueType type; + 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; + 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) + { + ExecuteIsNull(field); + return *this; + } + + DB_API Query &And() + { + ExecuteLogicOperation(QueryObjType::AND); + return *this; + } + + DB_API Query &Or() + { + ExecuteLogicOperation(QueryObjType::OR); + return *this; + } + + 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); + + friend class GetQueryInfo; + ~Query() {} + +private: + Query() {} + + 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); + + DB_SYMBOL void ExecuteLogicOperation(QueryObjType operType); + DB_SYMBOL void ExecuteOrderBy(const std::string &field, bool isAsc); + DB_SYMBOL void ExecuteLimit(int number, int offset); + DB_SYMBOL void ExecuteLike(const std::string &field, const std::string &value); + DB_SYMBOL void ExecuteNotLike(const std::string &field, const std::string &value); + DB_SYMBOL void ExecuteIsNull(const std::string &field); + + template + QueryValueType GetFieldTypeAndValue(const T &queryValue, FieldValue &fieldValue) + { + return GetQueryValueType::GetFieldTypeAndValue(queryValue, fieldValue); + } + + QueryExpression queryExpression_; +}; +} // namespace DistributedDB +#endif // DISTRIBUTEDDB_QUERY_H diff --git a/services/distributeddataservice/libs/distributeddb/include/query_expression.h b/services/distributeddataservice/libs/distributeddb/include/query_expression.h new file mode 100755 index 000000000..f1788d0c4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/include/query_expression.h @@ -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. + */ + +#ifndef DISTRIBUTEDDB_QUERY_EXPRESSION_H +#define DISTRIBUTEDDB_QUERY_EXPRESSION_H + +#include +#include +#include + +#include "types_export.h" + +namespace DistributedDB { +enum class QueryValueType { + VALUE_TYPE_INVALID = -1, + VALUE_TYPE_NULL, + VALUE_TYPE_BOOL, + VALUE_TYPE_INTEGER, + VALUE_TYPE_LONG, + VALUE_TYPE_DOUBLE, + VALUE_TYPE_STRING, +}; + +enum class QueryObjType { + OPER_ILLEGAL = -1, + QUERY_VALUE, + EQUALTO, + NOT_EQUALTO, + GREATER_THAN, + LESS_THAN, + GREATER_THAN_OR_EQUALTO, + LESS_THAN_OR_EQUALTO, + LIKE, + NOT_LIKE, + IS_NULL, + IS_NOT_NULL, + IN, + NOT_IN, + QUERY_BY_KEY_PREFIX, + BEGIN_GROUP, + END_GROUP, + AND, + OR, + LIMIT, + ORDERBY, + SUGGEST_INDEX, +}; + +struct QueryObjNode { + QueryObjType operFlag = QueryObjType::OPER_ILLEGAL; + std::string fieldName = ""; + QueryValueType type = QueryValueType::VALUE_TYPE_INVALID; + std::vector fieldValue = {}; +}; + +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(); + + std::string GetSuggestIndex(); + + const std::list &GetQueryExpression(); + + void SetErrFlag(bool flag); + bool GetErrFlag(); + +private: + void AssemblyQueryInfo(const QueryObjType querrOperType, 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_; +}; + +// 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/services/distributeddataservice/libs/distributeddb/include/types_export.h b/services/distributeddataservice/libs/distributeddb/include/types_export.h new file mode 100755 index 000000000..76e948904 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/include/types_export.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +}; + +using PermissionCheckCallback = std::function; + +using PermissionCheckCallbackV2 = 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; + } + + void operator=(const SecurityOption &rhs) + { + securityLabel = rhs.securityLabel; + securityFlag = rhs.securityFlag; + } +}; +} // namespace DistributedDB + +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 +#endif // DISTRIBUTEDDB_TYPES_EXPORT_H diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/include/get_query_info.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/get_query_info.h new file mode 100644 index 000000000..5adaf9d91 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_communicator.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_communicator.h new file mode 100755 index 000000000..cdeac53a5 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_communicator.h @@ -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. + */ + +#ifndef IPROCESSCOMMUNICATOR_H +#define IPROCESSCOMMUNICATOR_H + +#include +#include +#include +#include +#include "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. +}; + +// 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(); + } +}; +} // namespace DistributedDB + +#endif // IPROCESSCOMMUNICATOR_H diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_system_api_adapter.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/iprocess_system_api_adapter.h new file mode 100755 index 000000000..7a0ede831 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_changed_data.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_changed_data.h new file mode 100644 index 000000000..ce63ef04e --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate.h new file mode 100755 index 000000000..93be3e10d --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate_manager.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate_manager.h new file mode 100755 index 000000000..638a49660 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_delegate_manager.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 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 "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 susccess. + 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 susccess. + DB_API DBStatus CloseKvStore(KvStoreDelegate *kvStore); +#endif + + DB_API DBStatus CloseKvStore(KvStoreNbDelegate *kvStore); + + // Used to delete a KvStore, return OK if delete susccess. + 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); + + DB_API static DBStatus SetProcessSystemAPIAdapter(const std::shared_ptr &adapter); + +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_; + + KvStoreConfig kvStoreConfig_; + std::string appId_; + std::string userId_; + + mutable std::mutex mutex_; +}; +} // namespace DistributedDB + +#endif // KV_STORE_DELEGATE_MANAGER_H diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_errno.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_errno.h new file mode 100644 index 000000000..364df4b0f --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_conflict_data.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_conflict_data.h new file mode 100644 index 000000000..801382615 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_delegate.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_delegate.h new file mode 100755 index 000000000..02a0a9005 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_nb_delegate.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "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" + +namespace DistributedDB { +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, +}; + +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, emptye means full subscription + unsigned int mode = 0; // obsever mode + int conflictType = 0; + KvStoreNbConflictNotifier notifier = nullptr; + int conflictResolvePolicy = LAST_WIN; + }; + + 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; +}; +} // namespace DistributedDB + +#endif // KV_STORE_NB_DELEGATE_H diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_observer.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_observer.h new file mode 100644 index 000000000..ea74c0c4b --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_result_set.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_result_set.h new file mode 100644 index 000000000..afb0e8aa7 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_snapshot_delegate.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/kv_store_snapshot_delegate.h new file mode 100755 index 000000000..f13b4531a --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/include/types.h b/services/distributeddataservice/libs/distributeddb/interfaces/include/types.h new file mode 100755 index 000000000..3a1d93fb6 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/include/types.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 exist 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 coverd 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 +}; + +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 +}; + +enum ResolutionPolicyType { + AUTO_LAST_WIN = 0, // resolve conflicts by timestamp(default value) + CUSTOMER_RESOLUTION = 1 // resolve conflicts by user +}; + +using KvStoreCorruptionHandler = std::function; +} // namespace DistributedDB + +#endif // KV_STORE_TYPE_H diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.cpp new file mode 100755 index 000000000..3f57e9c7d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_changed_data_impl.h new file mode 100644 index 000000000..417dbd2b8 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.cpp new file mode 100755 index 000000000..987f92b40 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.cpp @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_impl.h new file mode 100755 index 000000000..9cf8b5752 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_manager.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_manager.cpp new file mode 100755 index 000000000..d3493a05d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_delegate_manager.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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; + +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); + properties.SetStringProp(KvDBProperties::DATA_DIR, storePath); + properties.SetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, option.createDirByStoreIdOnly); + properties.SetSchema(schema); + 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); + } + } + + 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 (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); + 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 }; + KvDBProperties properties; + int errCode = AutoLaunch::GetAutoLaunchProperties(param, properties); + if (errCode != E_OK) { + LOGE("[KvStoreManager] Enable auto launch failed:%d", errCode); + return TransferDBErrno(errCode); + } + + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(properties, notifier, option.observer, + option.conflictType, option.notifier); + 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); + int errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(hashIdentifier); + 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); +} + +std::string KvStoreDelegateManager::GetKvStoreIdentifier(const std::string &userId, const std::string &appId, + const std::string &storeId) +{ + if (!ParamCheckUtils::CheckStoreParameter(storeId, appId, userId)) { + return ""; + } + return DBCommon::TransferHashString(userId + "-" + appId + "-" + storeId); +} + +DBStatus KvStoreDelegateManager::SetProcessSystemAPIAdapter(const std::shared_ptr &adapter) +{ + return TransferDBErrno(RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter)); +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_errno.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_errno.cpp new file mode 100755 index 000000000..6c7df9c34 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_errno.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_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}, + }; +} + +DBStatus TransferDBErrno(int err) +{ + for (const auto &item : ERRNO_MAP) { + if (item.errCode == err) { + return item.status; + } + } + return DB_ERROR; +} +}; diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.cpp new file mode 100755 index 000000000..36e8b0bdc --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_conflict_data_impl.h new file mode 100644 index 000000000..ff382f7c9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp new file mode 100755 index 000000000..be459311f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.cpp @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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}, + }; + + 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) { + if (errCode == -E_BUSY) { + return BUSY; + } + + if (errCode == -E_INVALID_ARGS) { + return INVALID_ARGS; + } + + LOGE("[KvStoreNbDelegate] Sync data failed:%d", errCode); + return DB_ERROR; + } + 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::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 +{ + std::map result; + for (const auto &pair : statuses) { + DBStatus status = DB_ERROR; + static const std::map statusMap = { + { static_cast(SyncOperation::FINISHED_ALL), DBStatus::OK }, + { static_cast(SyncOperation::TIMEOUT), DBStatus::TIME_OUT }, + { static_cast(SyncOperation::PERMISSION_CHECK_FAILED), DBStatus::PERMISSION_CHECK_FORBID_SYNC }, + { static_cast(SyncOperation::COMM_ABNORMAL), DBStatus::COMM_FAILURE }, + { static_cast(SyncOperation::SECURITY_OPTION_CHECK_FAILURE), DBStatus::SECURITY_OPTION_CHECK_ERROR }, + { static_cast(SyncOperation::EKEYREVOKED_FAILURE), DBStatus::EKEYREVOKED_ERROR }, + { static_cast(SyncOperation::SCHEMA_INCOMPATIBLE), DBStatus::SCHEMA_MISMATCH }, + { static_cast(SyncOperation::BUSY_FAILURE), DBStatus::BUSY }, + }; + auto iter = statusMap.find(pair.second); + if (iter != statusMap.end()) { + status = iter->second; + } + result.insert(std::pair(pair.first, status)); + } + if (onComplete) { + onComplete(result); + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h new file mode 100755 index 000000000..241ca620f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_nb_delegate_impl.h @@ -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. + */ + +#ifndef KV_STORE_NB_DELEGATE_IMPL_H +#define KV_STORE_NB_DELEGATE_IMPL_H + +#include +#include +#include +#include + +#include "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 + DB_API DBStatus Sync(const std::vector &devices, SyncMode mode, + const std::function &devicesMap)> &onComplete, + bool wait) override; + + // Special pragma interface, see PragmaCmd and PragmaData, + DB_API DBStatus Pragma(PragmaCmd cmd, PragmaData ¶mData) override; + + // Set the conflict notifier for getting the specified type conflict data. + DB_API 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(); + +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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.cpp new file mode 100755 index 000000000..968284da0 --- /dev/null +++ b/services/distributeddataservice/libs/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) +{ + long long position = GetPosition(); + long long 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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_result_set_impl.h new file mode 100644 index 000000000..700434b16 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.cpp new file mode 100755 index 000000000..17ce4a8f9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h b/services/distributeddataservice/libs/distributeddb/interfaces/src/kv_store_snapshot_delegate_impl.h new file mode 100755 index 000000000..71d8ed5af --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb.h new file mode 100644 index 000000000..e21c0455e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb.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_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; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_connection.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_connection.h new file mode 100755 index 000000000..37ca17240 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_connection.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 I_KV_DB_CONNECTION_H +#define I_KV_DB_CONNECTION_H + +#include +#include + +#include "types.h" +#include "db_types.h" +#include "macro_utils.h" +#include "query.h" + +namespace DistributedDB { +class IKvDB; +class IKvDBSnapshot; +class KvDBObserverHandle; +class KvDBCommitNotifyData; +class IKvDBResultSet; + +using KvDBObserverAction = std::function; +using KvDBConflictAction = std::function; + +class IKvDBConnection { +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; +}; +} // namespace DistributedDB + +#endif // I_KV_DB_CONNECTION_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_factory.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_factory.h new file mode 100755 index 000000000..eaf5600bc --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_result_set.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_result_set.h new file mode 100755 index 000000000..b0aa5de38 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_snapshot.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_snapshot.h new file mode 100644 index 000000000..17d3fc82a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_sync_interface.h b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_sync_interface.h new file mode 100644 index 000000000..bd698dbe0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/ikvdb_sync_interface.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_KVDB_SYNC_INTERFACE_H +#define I_KVDB_SYNC_INTERFACE_H + +#include + +#include "db_types.h" +#include "kvdb_properties.h" + +namespace DistributedDB { +class IKvDBSyncInterface { +public: + enum { + SYNC_SVD = 1, // Single version data + SYNC_MVD, // Multi version data + }; + + // Constructor/Destructor. + IKvDBSyncInterface() = default; + virtual ~IKvDBSyncInterface() {} + + // 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 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; + + // Get all meta data keys. + virtual int GetAllMetaKeys(std::vector &keys) const = 0; + + virtual const KvDBProperties &GetDbProperties() const = 0; +}; +} // namespace DistributedDB + +#endif // I_KVDB_SYNC_INTERFACE_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_commit_notify_data.h b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_commit_notify_data.h new file mode 100644 index 000000000..2f82539a3 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_conflict_entry.h b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_conflict_entry.h new file mode 100644 index 000000000..79f83c3ea --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_manager.h b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_manager.h new file mode 100755 index 000000000..af8832202 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_manager.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 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" + +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(); +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 *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 KvDBManager *instance_; + static std::mutex kvDBLock_; + static std::mutex instanceLock_; + + 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/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_pragma.h b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_pragma.h new file mode 100755 index 000000000..c6ecc0464 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_pragma.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 KV_DB_PRAGMA_H +#define KV_DB_PRAGMA_H + +#include +#include +#include + +#include "types.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, +}; + +struct PragmaSync { + PragmaSync(const std::vector &devices, int mode, + const std::function &devicesMap)> &onComplete, + bool wait = false) + : devices_(devices), mode_(mode), onComplete_(onComplete), wait_(wait) {} + + std::vector devices_; + int mode_; + std::function &devicesMap)> onComplete_; + bool wait_; +}; + +struct PragmaRemotePushNotify { + PragmaRemotePushNotify(RemotePushFinishedNotifier notifier) : notifier_(notifier) {} + + RemotePushFinishedNotifier notifier_; +}; +} // namespace DistributedDB + +#endif // KV_DB_PRAGMA_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_properties.h b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_properties.h new file mode 100755 index 000000000..9c8a66383 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/kvdb_properties.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "schema_object.h" + +namespace DistributedDB { +class KvDBProperties final { +public: + KvDBProperties(); + ~KvDBProperties(); + + // Get the sub directory for different type database. + static std::string GetStoreSubDirectory(int type); + + // 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); + + // 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 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 FILE_NAME; + static const std::string SYNC_MODE; + static const std::string MEMORY_MODE; + static const std::string ENCRYPTED_MODE; + static const std::string IDENTIFIER_DATA; + static const std::string IDENTIFIER_DIR; + 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 int LOCAL_TYPE = 1; + static const int MULTI_VER_TYPE = 2; + static const int SINGLE_VER_TYPE = 3; + +private: + std::map stringProperties_; + std::map boolProperties_; + std::map intProperties_; + CipherType cipherType_; + CipherPassword password_; + SchemaObject schema_; +}; +} // namespace DistributedDB + +#endif // KV_DB_PROPERTIES_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_def.h b/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_def.h new file mode 100644 index 000000000..64dd3c6b9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h b/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_kvdb_sync_interface.h new file mode 100755 index 000000000..83e985b5c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum.h b/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum.h new file mode 100755 index 000000000..613a3dc9f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum.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 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/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum_executor.h b/services/distributeddataservice/libs/distributeddb/storage/include/multi_ver_vacuum_executor.h new file mode 100644 index 000000000..011260654 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/include/single_ver_kvdb_sync_interface.h b/services/distributeddataservice/libs/distributeddb/storage/include/single_ver_kvdb_sync_interface.h new file mode 100755 index 000000000..828c4de22 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/include/single_ver_kvdb_sync_interface.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 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" + +namespace DistributedDB { +class SingleVerKvDBSyncInterface : public IKvDBSyncInterface { +public: + ~SingleVerKvDBSyncInterface() override {}; + + // 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 &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const = 0; + + virtual int GetSyncData(TimeStamp begin, TimeStamp end, std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const = 0; + + // Get the data using the token allocated by GetSyncData, the token would be release automatically when finished. + virtual int GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const = 0; + + virtual int GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const = 0; + + // Release the continue token of getting data. + virtual void ReleaseContinueToken(ContinueToken &continueStmtToken) const = 0; + + // Put synced data from remote devices. + virtual int PutSyncData(std::vector &dataItems, const std::string &deviceName) = 0; + + virtual int PutSyncData(const std::vector &entries, const std::string &deviceName) = 0; + + virtual void ReleaseKvEntry(const SingleVerKvEntry *entry) = 0; + + virtual int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) = 0; + + virtual SchemaObject GetSchemaInfo() const = 0; + + virtual bool CheckCompatible(const std::string &schema) const = 0; + + virtual int GetSecurityOption(SecurityOption &option) const = 0; + + virtual bool IsReadable() const = 0; + + virtual void NotifyRemotePushFinished(const std::string &targetId) const = 0; +}; +} + +#endif // SINGLE_VER_KVDB_SYNC_INTERFACE_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/include/storage_engine_manager.h b/services/distributeddataservice/libs/distributeddb/storage/include/storage_engine_manager.h new file mode 100755 index 000000000..ad1178843 --- /dev/null +++ b/services/distributeddataservice/libs/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 StorageEngineManager *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/services/distributeddataservice/libs/distributeddb/storage/src/default_factory.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/default_factory.cpp new file mode 100755 index 000000000..0667c5521 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/default_factory.h b/services/distributeddataservice/libs/distributeddb/storage/src/default_factory.h new file mode 100755 index 000000000..fa3b4f73c --- /dev/null +++ b/services/distributeddataservice/libs/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() {} + ~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/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.cpp new file mode 100755 index 000000000..158a435d7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +{ + return -E_NOT_SUPPORT; +} + +int GenericKvDB::TransConflictTypeToRegisterFunctionType(int conflictType, RegisterFuncType &type) const +{ + 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 +{ + return; +} + +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; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.h b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.h new file mode 100755 index 000000000..17b9585b3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "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; + +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/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.cpp new file mode 100755 index 000000000..19935e922 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + 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) +{ + 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 +{ + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetEntries(const IOption &option, const Query &query, std::vector &entries) const +{ + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetResultSet(const IOption &option, const Key &keyPrefix, + IKvDBResultSet *&resultSet) const +{ + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetResultSet(const IOption &option, const Query &query, IKvDBResultSet *&resultSet) const +{ + return -E_NOT_SUPPORT; +} + +int GenericKvDBConnection::GetCount(const IOption &option, const Query &query, int &count) const +{ + return -E_NOT_SUPPORT; +} + +void GenericKvDBConnection::ReleaseResultSet(IKvDBResultSet *&resultSet) +{ + return; +} + +int GenericKvDBConnection::RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) +{ + 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); +} +} + diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.h b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.h new file mode 100755 index 000000000..91bac5720 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_kvdb_connection.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 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; +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/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.cpp new file mode 100755 index 000000000..da289231d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "db_errno.h" +#include "parcel.h" +#include "version.h" + +namespace DistributedDB { +namespace { + enum OperType { + SERIALIZE, + DESERIALIZE, + CAL_LEN, + }; +} // namespace + +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::GetValue(Value &value) const +{ + value = dataItem_.value; +} + +uint64_t GenericSingleVerKvEntry::GetFlag() const +{ + return dataItem_.flag; +} + +// 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(SERIALIZE, targetVersion, parcel, len); + if (errCode != E_OK) { + return errCode; + } + return errCode; +} + +int GenericSingleVerKvEntry::SerializeDatas(const std::vector &kvEntries, Parcel &parcel, + uint32_t targetVersion) +{ + LOGD("GenericSingleVerKvEntry::SerialDatas targetVersion:%d", targetVersion); + uint32_t size = kvEntries.size(); + int errCode = parcel.WriteUInt32(size); + if (errCode != E_OK) { + return errCode; + } + parcel.EightByteAlign(); + for (const auto &kvEntry : kvEntries) { + if (kvEntry == nullptr) { + continue; + } + errCode = kvEntry->SerializeData(parcel, targetVersion); + if (errCode != E_OK) { + return errCode; + } + } + return errCode; +} + +// this func should do compatible +uint32_t GenericSingleVerKvEntry::CalculateLen(uint32_t targetVersion) +{ + uint64_t len = 0; + int errCode = AdaptToVersion(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) +{ + LOGD("GenericSingleVerKvEntry::CalculateLen targetVersion:%d", 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(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(int 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 SERIALIZE: + errCode = SerializeDataByVersion(targetVersion, parcel); + break; + case DESERIALIZE: + errCode = DeSerializeByVersion(targetVersion, parcel, datalen); + break; + default: + LOGE("Unknown upgrade serialize oper!"); + return -E_UPGRADE_FAILED; + } + return errCode; +} + +int GenericSingleVerKvEntry::AdaptToVersion(int operType, uint32_t targetVersion, uint64_t &datalen) +{ + if (targetVersion < SOFTWARE_VERSION_EARLIEST || targetVersion > SOFTWARE_VERSION_CURRENT) { + return -E_VERSION_NOT_SUPPORT; + } + + if (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) const +{ + TimeStamp writeTimeStamp = dataItem_.writeTimeStamp; + if (writeTimeStamp == 0) { + writeTimeStamp = dataItem_.timeStamp; + } + + return parcel.WriteUInt64(writeTimeStamp); +} + +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); +} + +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) const +{ + len += Parcel::GetUInt64Len(); +} + +int GenericSingleVerKvEntry::CalLenByVersion(uint32_t targetVersion, uint64_t &len) const +{ + CalLenByFirstVersion(len); + if (targetVersion == SOFTWARE_VERSION_EARLIEST) { + return E_OK; + } + CalLenByLaterVersion(len); + 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) +{ + len += parcel.ReadUInt64(dataItem_.writeTimeStamp); +} + +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); + return E_OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.h b/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.h new file mode 100644 index 000000000..0dce405e3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/generic_single_ver_kv_entry.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 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 { +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; + + void GetValue(Value &value) const; + + uint64_t GetFlag() const override; + + void SetEntryData(DataItem &&dateItem); + + 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); + + static uint32_t CalculateLens(const std::vector &kvEntries, uint32_t targetVersion); + +private: + int AdaptToVersion(int operType, uint32_t targetVersion, Parcel &parcel, uint64_t &datalen); + int AdaptToVersion(int operType, uint32_t targetVersion, uint64_t &datalen); + + int SerializeDataByVersion(uint32_t targetVersion, Parcel &parcel) const; + int SerializeDataByFirstVersion(Parcel &parcel) const; + int SerializeDataByLaterVersion(Parcel &parcel) const; + + int CalLenByVersion(uint32_t targetVersion, uint64_t &len) const; + void CalLenByFirstVersion(uint64_t &len) const; + void CalLenByLaterVersion(uint64_t &len) 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); + + DataItem dataItem_; +}; +} // namespace DistributedDB + +#endif // GENERIC_SINGLE_VER_KV_ENTRY_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit.h b/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit.h new file mode 100644 index 000000000..eb254dc8c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit_storage.h b/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_commit_storage.h new file mode 100644 index 000000000..c6e7aeef8 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_factory.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_factory.cpp new file mode 100755 index 000000000..cfa42739d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_raw_cursor.h b/services/distributeddataservice/libs/distributeddb/storage/src/ikvdb_raw_cursor.h new file mode 100644 index 000000000..b306bf3e0 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.cpp new file mode 100755 index 000000000..3e635342e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.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 "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) +{ + 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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_commit_notify_filterable_data.h new file mode 100755 index 000000000..d53d06b78 --- /dev/null +++ b/services/distributeddataservice/libs/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 \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_manager.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_manager.cpp new file mode 100755 index 000000000..47fa951eb --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_manager.cpp @@ -0,0 +1,789 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "platform_specific.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" + +namespace DistributedDB { +const std::string KvDBManager::PROCESS_LABEL_CONNECTOR = "-"; +KvDBManager *KvDBManager::instance_ = nullptr; +std::mutex KvDBManager::kvDBLock_; +std::mutex KvDBManager::instanceLock_; + +namespace { + DefaultFactory g_defaultFactory; + + int CreateDataBase(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; + } + return errCode; + } + + int 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::RemoveDatabase(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) +{ + IKvDBFactory *factory = IKvDBFactory::GetCurrent(); + if (factory == nullptr) { + return -E_INVALID_DB; + } + + int 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(); +} + +// 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, ""); + manager->EnterDBOpenCloseProcess(identifier); + IKvDB *kvDB = manager->GetDataBase(properties, errCode, isNeedIfOpened); + if (kvDB == nullptr) { + if (isNeedIfOpened) { + LOGE("Failed to open the db:%d", errCode); + } + } else { + connection = kvDB->GetDBConnection(errCode); + if (connection == nullptr) { + 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 is corrupted:%d", 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); + } + return errCode; +} + +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; + } + } else { + if (isMemoryDb && !isCreateNecessary) { + LOGI("IsCreateNecessary is false, Not need create database"); + } else if (errCode == -E_NOT_FOUND) { + kvDB = OpenNewDatabase(property, errCode); + if (kvDB == nullptr) { + LOGE("Failed to open the new database."); + } + } + } + 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 [%d], size[%llu]", 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 = CreateDataBase(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]() { + 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) +{ + int errCode = CheckDatabaseFileStatus(properties); + if (errCode != E_OK) { + return errCode; + } + + errCode = ExecuteRemoveDatabase(properties); + if (errCode != E_OK) { + LOGE("[KvDBManager] Remove database failed:%d", errCode); + } + return errCode; +} + +std::string KvDBManager::GenerateKvDBIdentifier(const KvDBProperties &property) +{ + return property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); +} + +KvDBManager *KvDBManager::GetInstance() +{ + 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 type not matched!"); + 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 (!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; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.cpp new file mode 100755 index 000000000..9c089da5a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.h b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_observer_handle.h new file mode 100644 index 000000000..2833d10cf --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_properties.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_properties.cpp new file mode 100755 index 000000000..d13027d83 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_properties.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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::CREATE_IF_NECESSARY = "createIfNecessary"; +const std::string KvDBProperties::DATABASE_TYPE = "databaseType"; +const std::string KvDBProperties::DATA_DIR = "dataDir"; +const std::string KvDBProperties::USER_ID = "userId"; +const std::string KvDBProperties::APP_ID = "appId"; +const std::string KvDBProperties::STORE_ID = "storeId"; +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::IDENTIFIER_DATA = "identifier"; +const std::string KvDBProperties::IDENTIFIER_DIR = "identifierDir"; +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"; + +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"; + } +} + +std::string KvDBProperties::GetStringProp(const std::string &name, const std::string &defaultValue) const +{ + auto iter = stringProperties_.find(name); + if (iter != stringProperties_.end()) { + return iter->second; + } else { + return defaultValue; + } +} + +void KvDBProperties::SetStringProp(const std::string &name, const std::string &value) +{ + stringProperties_[name] = value; +} + +bool KvDBProperties::GetBoolProp(const std::string &name, bool defaultValue) const +{ + auto iter = boolProperties_.find(name); + if (iter != boolProperties_.end()) { + return iter->second; + } else { + return defaultValue; + } +} + +void KvDBProperties::SetBoolProp(const std::string &name, bool value) +{ + boolProperties_[name] = value; +} + +int KvDBProperties::GetIntProp(const std::string &name, int defaultValue) const +{ + auto iter = intProperties_.find(name); + if (iter != intProperties_.end()) { + return iter->second; + } else { + return defaultValue; + } +} + +void KvDBProperties::SetIntProp(const std::string &name, int value) +{ + intProperties_[name] = value; +} + +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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.cpp new file mode 100755 index 000000000..0cedc59e6 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.h b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_utils.h new file mode 100644 index 000000000..da294afdd --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.cpp new file mode 100755 index 000000000..ca269bf6c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.h b/services/distributeddataservice/libs/distributeddb/storage/src/kvdb_windowed_result_set.h new file mode 100755 index 000000000..2c5fe3b7d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/local_kvdb.h b/services/distributeddataservice/libs/distributeddb/storage/src/local_kvdb.h new file mode 100644 index 000000000..22f8fa5ae --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.cpp new file mode 100755 index 000000000..8c859c661 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/generic_multi_ver_kv_entry.h new file mode 100755 index 000000000..9fc13a46c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_data_storage.h new file mode 100755 index 000000000..f56d53b38 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/ikvdb_multi_ver_transaction.h new file mode 100755 index 000000000..81490fc68 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.cpp new file mode 100755 index 000000000..fbde2ffd5 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_commit.h new file mode 100755 index 000000000..eecf9860d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kv_entry.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kv_entry.h new file mode 100755 index 000000000..f03905002 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.cpp new file mode 100755 index 000000000..25857f603 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_kvdata_storage.h new file mode 100755 index 000000000..82cae19d3 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp new file mode 100755 index 000000000..fc2b15138 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.cpp @@ -0,0 +1,1182 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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(); + if (multiVerEngine_ == nullptr) { + errCode = -E_OUT_OF_MEMORY; + goto ERROR; + } + 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; +} + +// 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; +} + +DEFINE_OBJECT_TAG_FACILITIES(MultiVerNaturalStore) +} // namespace DistributedDB +#endif diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.h new file mode 100755 index 000000000..72594171d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + + // 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); + +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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.cpp new file mode 100755 index 000000000..2ece9cb43 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_notify_data.h new file mode 100755 index 000000000..90d6ee808 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.cpp new file mode 100755 index 000000000..85f76b10e --- /dev/null +++ b/services/distributeddataservice/libs/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:%ul.", 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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_commit_storage.h new file mode 100755 index 000000000..3a310de4f --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.cpp new file mode 100755 index 000000000..0820c9adc --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.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. + */ + +#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) { + errCode = E_OK; + } else { + 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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_connection.h new file mode 100755 index 000000000..86456481b --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.cpp new file mode 100755 index 000000000..5b11e3af0 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_snapshot.h new file mode 100755 index 000000000..86ae7e9bb --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.cpp new file mode 100755 index 000000000..acc4e7710 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_natural_store_transfer_data.h new file mode 100755 index 000000000..5fa07f715 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.cpp new file mode 100755 index 000000000..0ec1015d9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_engine.h new file mode 100755 index 000000000..933357112 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp new file mode 100755 index 000000000..048c953e8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.cpp @@ -0,0 +1,1500 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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:%llu", 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:%llu", 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:%llu", 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 %llu", 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:%llu", 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) +{ + return E_OK; +} + +int MultiVerStorageExecutor::UpdateTrimedFlag(const CommitID &commit) +{ + return E_OK; +} +} // namespace DistributedDB +#endif diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_storage_executor.h new file mode 100755 index 000000000..e1f4d22cc --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum.cpp new file mode 100755 index 000000000..e5b63ee42 --- /dev/null +++ b/services/distributeddataservice/libs/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=%d, Version=%llu, HashKey=%s.", record.type, ULL(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.", + 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.", + 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=%d, Version=%llu, HashKey=%s.", 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=%d, Version=%llu, HashKey=%s, errCode=%d.", + 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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.cpp new file mode 100755 index 000000000..c2047fe52 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_vacuum_executor_impl.h new file mode 100755 index 000000000..ae955ec84 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.cpp new file mode 100755 index 000000000..796f41661 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.h b/services/distributeddataservice/libs/distributeddb/storage/src/multiver/multi_ver_value_object.h new file mode 100755 index 000000000..ca62af3e9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.cpp new file mode 100755 index 000000000..ffd876fbc --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.cpp @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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)) { + LOGE("Rekey fail 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 (remove(fileName.c_str()) != 0) { + 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); + + LOGI("Clear the files while import"); + 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; + LOGI("Remove the exported files."); + 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/services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.h b/services/distributeddataservice/libs/distributeddb/storage/src/operation/database_oper.h new file mode 100755 index 000000000..5e933b0aa --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.cpp new file mode 100755 index 000000000..ceede762c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.h b/services/distributeddataservice/libs/distributeddb/storage/src/operation/local_database_oper.h new file mode 100755 index 000000000..dc0359ed7 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.cpp new file mode 100755 index 000000000..a7aeccc49 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + } + errCode = RekeyRecover(multiVerNaturalStore_->GetDbProperties()); + return E_OK; +} + +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/services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.h b/services/distributeddataservice/libs/distributeddb/storage/src/operation/multi_ver_database_oper.h new file mode 100755 index 000000000..eb2fa3100 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.cpp new file mode 100755 index 000000000..6863a736c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.h b/services/distributeddataservice/libs/distributeddb/storage/src/operation/single_ver_database_oper.h new file mode 100755 index 000000000..946409d7a --- /dev/null +++ b/services/distributeddataservice/libs/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 &dir, 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/services/distributeddataservice/libs/distributeddb/storage/src/package_file.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/package_file.cpp new file mode 100755 index 000000000..cc1625c6b --- /dev/null +++ b/services/distributeddataservice/libs/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 (remove(targetFile.c_str()) != EOK) { + 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/services/distributeddataservice/libs/distributeddb/storage/src/package_file.h b/services/distributeddataservice/libs/distributeddb/storage/src/package_file.h new file mode 100755 index 000000000..c404091e4 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.cpp new file mode 100755 index 000000000..775ef833a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.h b/services/distributeddataservice/libs/distributeddb/storage/src/result_entries_window.h new file mode 100644 index 000000000..7b0fe2705 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_kv_entry.h b/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_kv_entry.h new file mode 100644 index 000000000..75ce5ff47 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_kv_entry.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_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; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_KV_ENTRY_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.cpp new file mode 100755 index 000000000..2aec796fe --- /dev/null +++ b/services/distributeddataservice/libs/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 &itemOri) 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 != itemOri.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/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h b/services/distributeddataservice/libs/distributeddb/storage/src/single_ver_natural_store_commit_notify_data.h new file mode 100755 index 000000000..be54d608a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.cpp new file mode 100755 index 000000000..bb7aeb6c4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "schema_utils.h" +#include "get_query_info.h" +#include "log_print.h" +#include "sqlite_utils.h" +#include "db_constant.h" +#include "macro_utils.h" + +namespace DistributedDB { +namespace { + const std::string PRE_QUERY_KV_SQL = "SELECT key, value FROM sync_data "; + 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 USING_INDEX = "INDEXED BY "; + const int MAX_SQL_LEN = 1024 * 1024; // 1M bytes + const int LIMIT_FIELD_VALUE_SIZE = 2; + const int SINGLE_FIELD_VALUE_SIZE = 1; + const int INVALID_LIMIT = INT_MAX; + const int MAX_CONDITIONS_SIZE = 128; + const int MAX_SQLITE_BIND_SIZE = 50000; + + enum SymbolType { + INVALID_SYMBOL = -1, + COMPARE_SYMBOL, // relation symbol use to compare + RELATIONAL_SYMBOL, + RANGE_SYMBOL, + LOGIC_SYMBOL, + LINK_SYMBOL, // use to link relatonal symbol + SPECIAL_SYMBOL, // need special precess and need at the last + PREFIXKEY_SYMBOL, + SUGGEST_INDEX_SYMBOL, + }; + + bool IsNeedCheckEqualFormat(SymbolType type) + { + return type == COMPARE_SYMBOL || type == RELATIONAL_SYMBOL || type == RANGE_SYMBOL; + } + + // Considering our company's style, give up hexadecimal expressions state machines to avoid unreadable + const std::map SYMBOL_TYPE_DIC { + {QueryObjType::EQUALTO, COMPARE_SYMBOL}, + {QueryObjType::NOT_EQUALTO, COMPARE_SYMBOL}, + {QueryObjType::GREATER_THAN, COMPARE_SYMBOL}, + {QueryObjType::LESS_THAN, COMPARE_SYMBOL}, + {QueryObjType::GREATER_THAN_OR_EQUALTO, COMPARE_SYMBOL}, + {QueryObjType::LESS_THAN_OR_EQUALTO, COMPARE_SYMBOL}, + {QueryObjType::LIKE, RELATIONAL_SYMBOL}, + {QueryObjType::NOT_LIKE, RELATIONAL_SYMBOL}, + {QueryObjType::IS_NULL, RELATIONAL_SYMBOL}, + {QueryObjType::IS_NOT_NULL, RELATIONAL_SYMBOL}, + {QueryObjType::IN, RANGE_SYMBOL}, + {QueryObjType::NOT_IN, RANGE_SYMBOL}, + {QueryObjType::BEGIN_GROUP, LOGIC_SYMBOL}, + {QueryObjType::END_GROUP, LOGIC_SYMBOL}, + {QueryObjType::AND, LINK_SYMBOL}, + {QueryObjType::OR, LINK_SYMBOL}, + {QueryObjType::LIMIT, SPECIAL_SYMBOL}, + {QueryObjType::ORDERBY, SPECIAL_SYMBOL}, + {QueryObjType::QUERY_BY_KEY_PREFIX, PREFIXKEY_SYMBOL}, + {QueryObjType::SUGGEST_INDEX, SUGGEST_INDEX_SYMBOL} + }; + + SymbolType GetSymbolType(const QueryObjType &queryObjType) + { + if (SYMBOL_TYPE_DIC.find(queryObjType) == SYMBOL_TYPE_DIC.end()) { + return INVALID_SYMBOL; + } + return SYMBOL_TYPE_DIC.at(queryObjType); + } + + 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, ")"}, + }; + + int CheckLinkerBefor(std::list::iterator &iter) + { + auto preIter = std::prev(iter, 1); + SymbolType symbolType = GetSymbolType(preIter->operFlag); + if (symbolType != COMPARE_SYMBOL && symbolType != RELATIONAL_SYMBOL && symbolType != LOGIC_SYMBOL && + symbolType != RANGE_SYMBOL && symbolType != PREFIXKEY_SYMBOL) { + LOGE("Must be a comparison operation before the connective! operFage = %s", VNAME(preIter->operFlag)); + return -E_INVALID_QUERY_FORMAT; + } + return E_OK; + } +} + +QueryObject::QueryObject() + : limit_(INVALID_LIMIT), + offset_(0), + orderByCounts_(0), + isValid_(true), + transformed_(false), + hasOrderBy_(false), + hasLimit_(false), + isOrderByAppeared_(false), + hasPrefixKey_(false), + isNeedOrderbyKey_(true) +{} + +QueryObject::QueryObject(const Query &query) + : limit_(INVALID_LIMIT), offset_(0), orderByCounts_(0), transformed_(false), hasOrderBy_(false), + hasLimit_(false), isOrderByAppeared_(false), hasPrefixKey_(false), isNeedOrderbyKey_(true) +{ + QueryExpression queryExpressions = GetQueryInfo::GetQueryExpression(query); + queryObjNodes_ = queryExpressions.GetQueryExpression(); + isValid_ = queryExpressions.GetErrFlag(); + prefixKey_ = queryExpressions.GetPreFixKey(); + suggestIndex_ = queryExpressions.GetSuggestIndex(); +} + +int QueryObject::GetQuerySql(std::string &sql, bool onlyRowid) +{ + int errCode = E_OK; + if (!IsValid(errCode)) { + return errCode; + } + + const std::string &querySqlForUse = (onlyRowid ? PRE_QUERY_ROWID_SQL : PRE_QUERY_KV_SQL); + sql = AssembleSqlForSuggestIndex(querySqlForUse); + sql = !hasPrefixKey_ ? sql : (sql + " AND (key>=? AND key<=?) "); + if (transformed_) { + LOGD("This query object has been parsed."); + sql += querySql_; + return E_OK; + } + errCode = ToQuerySql(); + if (errCode != E_OK) { + LOGE("To query sql fail! errCode[%d]", errCode); + return errCode; + } + transformed_ = true; + sql += querySql_; + return errCode; +} + +int QueryObject::GetCountQuerySql(std::string &sql) +{ + int errCode = E_OK; + if (!IsValid(errCode)) { + return errCode; + } + + errCode = ToGetCountSql(); + if (errCode != E_OK) { + return errCode; + } + sql = AssembleSqlForSuggestIndex(PRE_GET_COUNT_SQL); + sql = !hasPrefixKey_ ? sql : (sql + " AND (key>=? AND key<=?) "); + sql += countSql_; + return E_OK; +} + +void QueryObject::SetSchema(const SchemaObject &schema) +{ + schema_ = schema; +} + +bool QueryObject::IsValid(int &errCode) +{ + if (!isValid_) { + errCode = -E_INVALID_QUERY_FORMAT; + LOGE("Invalid query object!"); + return isValid_; + } + errCode = CheckQueryLegality(); + if (errCode != E_OK) { + LOGE("Check query object illegal!"); + isValid_ = false; + } + return isValid_; +} + +bool QueryObject::IsCountValid() const +{ + if (hasLimit_ || hasOrderBy_) { + LOGI("It is invalid for limit and orderby!"); + return false; + } + return true; +} + +bool QueryObject::HasLimit() const +{ + return hasLimit_; +} + +void QueryObject::GetLimitVal(int &limit, int &offset) const +{ + limit = limit_; + offset = offset_; +} + +void QueryObject::FilterSymbolToAddBracketLink(bool &isNeedEndBracket, std::string &querySql) const +{ + for (const auto &iter : queryObjNodes_) { + SymbolType symbolType = GetSymbolType(iter.operFlag); + if (symbolType == COMPARE_SYMBOL || symbolType == RELATIONAL_SYMBOL || symbolType == RANGE_SYMBOL) { + querySql += " AND ("; + isNeedEndBracket = true; + break; + } else if (symbolType == LOGIC_SYMBOL || symbolType == PREFIXKEY_SYMBOL) { + continue; + } else { + break; + } + } +} + +int QueryObject::ToQuerySql() +{ + if (queryObjNodes_.empty()) { + querySql_ += ";"; + return E_OK; + } + + bool isNeedEndBracket = false; + QueryObject::FilterSymbolToAddBracketLink(isNeedEndBracket, 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_ += ") "; + } + + // 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 QueryObject::ToGetCountSql() +{ + countSql_.clear(); + if (queryObjNodes_.empty()) { + countSql_ += ";"; + return E_OK; + } + bool isNeedEndBracket = false; + QueryObject::FilterSymbolToAddBracketLink(isNeedEndBracket, 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; + if (objNode.fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) { + limit_ = objNode.fieldValue[0].integerValue; + offset_ = objNode.fieldValue[1].integerValue; + } + 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 QueryObject::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) { + LOGE("[Query] Get statement when bind prefix key, errCode = %d", errCode); + return errCode; + } + index = 3; // begin from 3rd args + } + + 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 QueryObject::GetCountSqlStatement(sqlite3 *dbHandle, const std::string &countSql, sqlite3_stmt *&countStmt) +{ + // bind statement for count + int 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 + } + + 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 QueryObject::CheckEqualFormat(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 = %d", errCode, schemaFieldType); + return -E_INVALID_QUERY_FIELD; + } + + if (schemaFieldType == FieldType::LEAF_FIELD_BOOL && 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 = 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(std::list::iterator &iter) const +{ + if (iter == 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 = GetSymbolType(nextIter->operFlag); + if (symbolType == INVALID_SYMBOL || symbolType == LINK_SYMBOL || symbolType == SPECIAL_SYMBOL) { + LOGE("Must be followed by comparison operation! operflag[%d], symbolType[%d]", nextIter->operFlag, symbolType); + return -E_INVALID_QUERY_FORMAT; + } + + return CheckLinkerBefor(iter); +} + +int QueryObject::CheckOrderByFormat(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(std::list::iterator &iter) const +{ + std::list::iterator next = std::next(iter, 1); + if (next != queryObjNodes_.end() && GetSymbolType(next->operFlag) != SUGGEST_INDEX_SYMBOL) { + LOGE("Limit should be last node or just before suggest-index nod!"); + return -E_INVALID_QUERY_FORMAT; + } + return E_OK; +} + +int QueryObject::CheckSuggestIndexFormat(std::list::iterator &iter) const +{ + std::list::iterator 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::CheckExpressionFormat(std::list::iterator &iter) +{ + SymbolType symbolType = GetSymbolType(iter->operFlag); + // The object is newly instantiated in the connection, and there is no reentrancy problem. + if (symbolType == PREFIXKEY_SYMBOL && hasPrefixKey_) { + LOGE("Only filt by prefix key once!!"); + return -E_INVALID_QUERY_FORMAT; + } + + if (iter->operFlag == QueryObjType::OPER_ILLEGAL || iter->type == QueryValueType::VALUE_TYPE_INVALID) { + return -E_INVALID_QUERY_FORMAT; + } else if (IsNeedCheckEqualFormat(symbolType)) { + return CheckEqualFormat(iter); + } else if (symbolType == LINK_SYMBOL) { + return CheckLinkerFormat(iter); + } else if (iter->operFlag == QueryObjType::LIMIT) { + hasLimit_ = true; + return CheckLimitFormat(iter); + } else if (iter->operFlag == QueryObjType::ORDERBY) { + return CheckOrderByFormat(iter); + } else if (symbolType == PREFIXKEY_SYMBOL) { + hasPrefixKey_ = true; + if (prefixKey_.size() > DBConstant::MAX_KEY_SIZE) { + return -E_INVALID_ARGS; + } + } else if (symbolType == SUGGEST_INDEX_SYMBOL) { + return CheckSuggestIndexFormat(iter); + } + return E_OK; +} + +int QueryObject::CheckQueryLegality() +{ + hasPrefixKey_ = false; // For Check preFixkey once + orderByCounts_ = 0; + + auto iter = queryObjNodes_.begin(); + int errCode = E_OK; + while (iter != queryObjNodes_.end()) { + errCode = CheckExpressionFormat(iter); + if (errCode != E_OK) { + return errCode; + } + iter++; + } + return errCode; +} + +std::string QueryObject::MapRelationalSymbolToSql(const QueryObjNode &queryNode) +{ + if (RELATIONAL_SYMBOL_TO_SQL.find(queryNode.operFlag) == RELATIONAL_SYMBOL_TO_SQL.end()) { + return ""; + }; + std::string sql = RELATIONAL_SYMBOL_TO_SQL.at(queryNode.operFlag) + MapValueToSql(queryNode); + if (GetSymbolType(queryNode.operFlag) == RANGE_SYMBOL) { + sql += ")"; + } + return sql; +} + +std::string QueryObject::MapLogicSymbolToSql(const QueryObjNode &queryNode) const +{ + if (LOGIC_SYMBOL_TO_SQL.find(queryNode.operFlag) == LOGIC_SYMBOL_TO_SQL.end()) { + return ""; + } + return LOGIC_SYMBOL_TO_SQL.at(queryNode.operFlag); +} + +std::string QueryObject::MapKeywordSymbolToSql(const QueryObjNode &queryNode) +{ + std::string sql; + switch (queryNode.operFlag) { + case QueryObjType::ORDERBY: + if (queryNode.fieldValue.size() == SINGLE_FIELD_VALUE_SIZE) { + if (!isOrderByAppeared_) { + sql += "ORDER BY "; + } + + sql += QueryObject::MapCastFuncSql(queryNode); + sql += queryNode.fieldValue[0].boolValue ? "ASC," : "DESC,"; + orderByCounts_--; + if (orderByCounts_ == 0) { + sql.pop_back(); + } + isOrderByAppeared_ = true; + } + return sql; + case QueryObjType::LIMIT: + if (queryNode.fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) { + if (hasPrefixKey_ && !hasOrderBy_ && isNeedOrderbyKey_) { + sql += "ORDER BY key ASC "; + } + sql += " LIMIT "; + sql += std::to_string(queryNode.fieldValue[0].integerValue); + sql += " OFFSET "; + sql += std::to_string(queryNode.fieldValue[1].integerValue); + } + return sql; + default: + return ""; + } + return sql; +} + +std::string QueryObject::MapValueToSql(const QueryObjNode &queryNode) +{ + std::string resultSql; + for (size_t i = 0; i < queryNode.fieldValue.size(); i++) { + if (i == 0) { + resultSql += " ? "; + } else { + resultSql += ", ? "; + } + } + return resultSql; +} + +static bool IsNeedCastWitEmptyValue(const QueryObjNode &queryNode) +{ + return (queryNode.operFlag == QueryObjType::IS_NULL || queryNode.operFlag == QueryObjType::IS_NOT_NULL || + queryNode.operFlag == QueryObjType::IN || queryNode.operFlag == QueryObjType::NOT_IN); +} + +std::string QueryObject::MapCastFuncSql(const QueryObjNode &queryNode) +{ + std::string resultSql; + if (queryNode.fieldValue.empty() && !IsNeedCastWitEmptyValue(queryNode)) { + return resultSql; + } + // fieldPath and isQueryable had been checked ok in the previous code, So here parse path and get type won't fail. + FieldPath fieldPath; + SchemaUtils::ParseAndCheckFieldPath(queryNode.fieldName, fieldPath); + FieldType fieldType = FieldType::LEAF_FIELD_INTEGER; + schema_.CheckQueryableAndGetFieldType(fieldPath, fieldType); + resultSql += SchemaObject::GenerateExtractSQL(schema_.GetSchemaType(), fieldPath, fieldType, schema_.GetSkipSize()); + isNeedOrderbyKey_ = false; // When index by value, No need order by key + return resultSql; +} + +int QueryObject::BindFieldValue(sqlite3_stmt *statement, const QueryObjNode &queryNode, int &index) const +{ + int errCode = E_OK; + SymbolType symbolType = GetSymbolType(queryNode.operFlag); + if (symbolType != COMPARE_SYMBOL && symbolType != RELATIONAL_SYMBOL && symbolType != RANGE_SYMBOL) { + return errCode; + } + + for (size_t i = 0; i < queryNode.fieldValue.size(); i++) { + if (queryNode.type == QueryValueType::VALUE_TYPE_BOOL) { + errCode = sqlite3_bind_int(statement, index, queryNode.fieldValue[i].boolValue); + } else if (queryNode.type == QueryValueType::VALUE_TYPE_INTEGER) { + errCode = sqlite3_bind_int(statement, index, queryNode.fieldValue[i].integerValue); + } else if (queryNode.type == QueryValueType::VALUE_TYPE_LONG) { + errCode = sqlite3_bind_int64(statement, index, queryNode.fieldValue[i].longValue); + } else if (queryNode.type == QueryValueType::VALUE_TYPE_DOUBLE) { + errCode = sqlite3_bind_double(statement, index, queryNode.fieldValue[i].doubleValue); + } else { + if (queryNode.fieldValue[i].stringValue.size() > 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 QueryObject::MapCastTypeSql(const FieldType &type) +{ + 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 ""; + } +} + +int QueryObject::ParseQueryExpression(const QueryObjNode &queryNode, std::string &querySql) +{ + 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 += MapCastFuncSql(queryNode); + querySql += MapRelationalSymbolToSql(queryNode); + } 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 QueryObject::AssembleSqlForSuggestIndex(const std::string &baseSql) const +{ + std::string formatIndex = CheckAndFormatSuggestIndex(); + if (formatIndex.empty()) { + return baseSql + FILTER_NATIVE_DATA_SQL; + } + + return baseSql + USING_INDEX + "'" + formatIndex + "' " + FILTER_NATIVE_DATA_SQL; +} + +std::string QueryObject::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); +} +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.h new file mode 100755 index 000000000..4884867e9 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/query_object.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 QUERY_OBJECT_H +#define QUERY_OBJECT_H + +#include +#include "schema_object.h" +#include "query.h" +#include "sqlite_import.h" + +namespace DistributedDB { +class QueryObject { +public: + QueryObject(); + explicit QueryObject(const Query &query); + ~QueryObject() = default; + int GetQuerySql(std::string &sql, bool onlyRowid = false); + int GetCountQuerySql(std::string &sql); + bool IsValid(int &errCode); + bool IsCountValid() const; + bool HasLimit() const; + void GetLimitVal(int &limit, int &offset) const; + void SetSchema(const SchemaObject &schema); + int GetQuerySqlStatement(sqlite3 *dbHandle, const std::string &sql, sqlite3_stmt *&statement); + int GetCountSqlStatement(sqlite3 *dbHandle, const std::string &countSql, sqlite3_stmt *&countStmt); +private: + int ToQuerySql(); + int ToGetCountSql(); + int CheckEqualFormat(std::list::iterator &iter) const; + int CheckLinkerFormat(std::list::iterator &iter) const; + int CheckOrderByFormat(std::list::iterator &iter); + int CheckExpressionFormat(std::list::iterator &iter); + int CheckLimitFormat(std::list::iterator &iter) const; + int CheckSuggestIndexFormat(std::list::iterator &iter) const; + int CheckQueryLegality(); + int ParseQueryExpression(const QueryObjNode &queryNode, std::string &querySql); + static std::string MapRelationalSymbolToSql(const QueryObjNode &queryNode); + std::string MapKeywordSymbolToSql(const QueryObjNode &queryNode); + std::string MapLogicSymbolToSql(const QueryObjNode &queryNode) const; + static std::string MapValueToSql(const QueryObjNode &queryNode); + std::string MapCastFuncSql(const QueryObjNode &queryNode); + static std::string MapCastTypeSql(const FieldType &type); + int BindFieldValue(sqlite3_stmt *statement, const QueryObjNode &queryNode, int &index) const; + void FilterSymbolToAddBracketLink(bool &isNeedEndBracket, std::string &querySql) const; + std::string AssembleSqlForSuggestIndex(const std::string &baseSql) const; + std::string CheckAndFormatSuggestIndex() const; + SchemaObject schema_; + std::list queryObjNodes_; + std::vector prefixKey_; + std::string querySql_; + std::string countSql_; + std::string suggestIndex_; + + int limit_; + int offset_; + 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 +}; +} +#endif diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_import.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_import.h new file mode 100755 index 000000000..a21885cb8 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.cpp new file mode 100755 index 000000000..c7816daa9 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb.h new file mode 100644 index 000000000..631497a23 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.cpp new file mode 100755 index 000000000..4379fa680 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_connection.h new file mode 100644 index 000000000..f4efd05b6 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.cpp new file mode 100755 index 000000000..430ebd605 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_kvdb_snapshot.h new file mode 100644 index 000000000..c94962f75 --- /dev/null +++ b/services/distributeddataservice/libs/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 &entry) const override; + + void Close(); + +private: + IKvDBConnection *connect_; +}; +} // namespace DistributedDB + +#endif // SQLITE_LOCAL_KV_DB_SNAP_SHOT_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.cpp new file mode 100755 index 000000000..872887855 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_engine.h new file mode 100644 index 000000000..d36ea4b1f --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.cpp new file mode 100755 index 000000000..c6bbc7640 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_local_storage_executor.h new file mode 100755 index 000000000..a1e6b5d29 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp new file mode 100755 index 000000000..762b4ab4b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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? +} + +// 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_data_storage.h new file mode 100755 index 000000000..8fca392b5 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.cpp new file mode 100755 index 000000000..53712b1e8 --- /dev/null +++ b/services/distributeddataservice/libs/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:%llu ", 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:%llu - %llu", 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:%llu", 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 :%llu vs %llu, %llu vs %llu, readVersion:%llu, version:%llu, %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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_multi_ver_transaction.h new file mode 100755 index 000000000..72c03751d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.cpp new file mode 100755 index 000000000..3ad498707 --- /dev/null +++ b/services/distributeddataservice/libs/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]version=%d, metaSplit[%d], secLabel[%d], secFlag[%d]", + version, 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 &parentDir) +{ + subDir_ = parentDir; +} + +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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_database_upgrader.h new file mode 100755 index 000000000..28ce76587 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.cpp new file mode 100755 index 000000000..dc282384a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_forward_cursor.h new file mode 100644 index 000000000..49ccdaf1d --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp new file mode 100755 index 000000000..8f38ccbb0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.cpp @@ -0,0 +1,1811 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "db_common.h" +#include "db_constant.h" +#include "sqlite_utils.h" +#include "storage_engine_manager.h" +#include "sqlite_single_ver_storage_engine.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "db_errno.h" +#include "log_print.h" +#include "value_hash_calc.h" +#include "hash.h" +#include "platform_specific.h" +#include "db_constant.h" +#include "package_file.h" +#include "generic_single_ver_kv_entry.h" +#include "schema_object.h" +#include "kvdb_utils.h" +#include "single_ver_database_oper.h" + +namespace DistributedDB { +#define CHECK_STORAGE_ENGINE do { \ + if (storageEngine_ == nullptr) { \ + return -E_INVALID_DB; \ + } \ +} while (0) + +namespace { + constexpr int MAX_SYNC_BLOCK_SIZE = 31457280; // 30MB + constexpr int DEF_LIFE_CYCLE_TIME = 60000; // 60s + constexpr int WAIT_DELEGATE_CALLBACK_TIME = 100; + + // 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(SQLiteSingleVerStorageExecutor *handle, std::vector &dataItems, + const DeviceInfo &deviceInfo, TimeStamp &maxTimestamp, SingleVerNaturalStoreCommitNotifyData *commitData) + { + int errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + + int innerCode; + 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); + 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 + } + return errCode; + } + + void ProcessContinueToken(int &errCode, TimeStamp end, const std::vector &dataItems, + ContinueTokenStruct *&token) + { + if (token == nullptr) { + return; + } + if (!dataItems.empty()) { + TimeStamp timestamp = dataItems.back().timeStamp; + if (timestamp > INT64_MAX - 1) { + token->SetBeginTimeStamp(INT64_MAX); + } else { + token->SetBeginTimeStamp(timestamp + 1); // Add 1 for the timestamp. + } + token->SetEndTimeStamp(end); + } else { + delete token; + token = nullptr; + errCode = -E_INTERNAL_ERROR; + } + } + + 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); + } +} + +SQLiteSingleVerNaturalStore::SQLiteSingleVerNaturalStore() + : currentMaxTimeStamp_(0), + storageEngine_(nullptr), + notificationEventsRegistered_(false), + notificationConflictEventsRegistered_(false), + isInitialized_(false), + isReadOnly_(false), + lifeCycleNotifier_(nullptr), + lifeTimerId_(0), + autoLifeTime_(DEF_LIFE_CYCLE_TIME) +{} + +SQLiteSingleVerNaturalStore::~SQLiteSingleVerNaturalStore() +{ + ReleaseResources(); +} + +std::string SQLiteSingleVerNaturalStore::GetDatabasePath(const KvDBProperties &kvDBProp) +{ + std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, ""); + std::string identifierDir = kvDBProp.GetStringProp(KvDBProperties::IDENTIFIER_DIR, ""); + std::string filePath = dataDir + "/" + identifierDir + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + 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 (notifier && 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; +} + +bool SQLiteSingleVerNaturalStore::IsReadable() const +{ + return true; +} + +namespace { +inline bool OriValueCanBeUse(int errCode) +{ + return (errCode == -E_VALUE_MATCH); +} + +inline bool AmendValueShouldBeUse(int errCode) +{ + return (errCode == -E_VALUE_MATCH_AMENDED); +} + +inline bool ValueIsSomehowWrong(int errCode) +{ + if (errCode == -E_VALUE_MISMATCH_FEILD_COUNT || + errCode == -E_VALUE_MISMATCH_FEILD_TYPE || + errCode == -E_VALUE_MISMATCH_CONSTRAINT) { + return true; + } + return false; +} +} + +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 (ValueIsSomehowWrong(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) +{ + 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::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; + storageEngine_ = + static_cast(StorageEngineManager::GetStorageEngine(kvDBProp, errCode)); + if (errCode != E_OK) { + goto ERROR; + } + + if (storageEngine_->IsEngineCorrupted()) { + errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB; + LOGE("[SqlSinStore][Open] database engine is corrupted, not need continue to open! errCode = [%d]", errCode); + goto ERROR; + } + + errCode = InitDatabaseContext(kvDBProp); + if (errCode != E_OK) { + LOGE("[SqlSinStore][Open] Init database context fail! errCode = [%d]", errCode); + goto ERROR; + } + errCode = RegisterNotification(); + if (errCode != E_OK) { + LOGE("Register notification failed:%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(); + InitialLocalDataTimestamp(); + isInitialized_ = true; + isReadOnly_ = isReadOnly; + return E_OK; +ERROR: + ReleaseResources(); + return errCode; +} + +void SQLiteSingleVerNaturalStore::Close() +{ + SyncAbleKvDB::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; +} + +// Get interface for syncer. +IKvDBSyncInterface *SQLiteSingleVerNaturalStore::GetSyncInterface() +{ + return this; +} + +int SQLiteSingleVerNaturalStore::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); + 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; +} + +int SQLiteSingleVerNaturalStore::GetAllMetaKeys(std::vector &keys) const +{ + CHECK_STORAGE_ENGINE; + 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) { + for (auto &itemEntry : entries) { + delete itemEntry; + itemEntry = nullptr; + } + entries.clear(); + } + HeartBeatForLifeCycle(); + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncData(TimeStamp begin, TimeStamp end, std::vector &dataItems, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + if (begin >= end || dataSizeInfo.blockSize > MAX_SYNC_BLOCK_SIZE) { + return -E_INVALID_ARGS; + } + + auto token = new (std::nothrow) ContinueTokenStruct; + 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) { + if (errCode != E_OK) { + dataItems.clear(); + } + delete token; + token = nullptr; + } else { + ProcessContinueToken(errCode, end, dataItems, token); + } + + if (handle != nullptr) { + ReleaseHandle(handle); + } + continueStmtToken = static_cast(token); + 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; + errCode = GetSyncDataNext(dataItems, continueStmtToken, dataSizeInfo); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + LOGE("GetSyncDataNext errCode:%d", errCode); + goto ERROR; + } + + for (auto &item : dataItems) { + GenericSingleVerKvEntry *entry = new (std::nothrow) GenericSingleVerKvEntry(); + if (entry == nullptr) { + errCode = -E_OUT_OF_MEMORY; + LOGE("GetSyncDataNext errCode:%d", errCode); + goto ERROR; + } + entry->SetEntryData(std::move(item)); + entries.push_back(entry); + } + +ERROR: + if (errCode != E_OK && errCode != -E_UNFINISHED) { + for (auto &itemEntry : entries) { + delete itemEntry; + itemEntry = nullptr; + } + entries.clear(); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::GetSyncDataNext(std::vector &dataItems, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const +{ + if (dataSizeInfo.blockSize > 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->GetBeginTimeStamp(), + token->GetEndTimeStamp(), dataSizeInfo); + if (errCode == -E_UNFINISHED) { + // update the begin timestamp of the next fetch. + if (!dataItems.empty()) { + TimeStamp timestamp = dataItems.back().timeStamp; + if (timestamp >= INT64_MAX) { + token->SetBeginTimeStamp(INT64_MAX); + } else { + token->SetBeginTimeStamp(timestamp + 1); // Add 1 for the next fetch. + } + continueStmtToken = static_cast(token); + } else { + ReleaseContinueToken(continueStmtToken); + errCode = -E_INTERNAL_ERROR; + } + } else { + ReleaseContinueToken(continueStmtToken); + if (errCode == -E_FINISHED) { + errCode = E_OK; + } + } + 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; + return; +} + +int SQLiteSingleVerNaturalStore::PutSyncData(const std::vector &entries, + const std::string &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); + dataItems.push_back(item); + } + } + HeartBeatForLifeCycle(); + return PutSyncData(dataItems, deviceName); +} + +int SQLiteSingleVerNaturalStore::PutSyncData(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; + } + DeviceInfo deviceInfo = {false, deviceName}; + if (deviceName.empty()) { + deviceInfo.deviceName = "Unknown"; + } + + int errCode = SaveSyncDataItems(dataItems, deviceInfo, true); // Currently true to check value content + if (errCode != E_OK) { + LOGE("PutSyncData errCode:%d", errCode); + } + + return errCode; +} + +void SQLiteSingleVerNaturalStore::ReleaseKvEntry(const SingleVerKvEntry *entry) +{ + if (entry != nullptr) { + delete entry; + entry = nullptr; + } +} + +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; + } + // Call the syncer module to erase the water mark. + int errCode = EraseDeviceWaterMark(deviceName); + 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 : %d", 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() +{ + int errCode = RegisterNotificationEventType(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + if (errCode != E_OK) { + LOGE("Register single version local event failed:%d!", errCode); + return errCode; + } + + errCode = RegisterNotificationEventType(SQLITE_GENERAL_NS_PUT_EVENT); + if (errCode != E_OK) { + LOGE("Register single version put event failed:%d!", errCode); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + return errCode; + } + + errCode = RegisterNotificationEventType(SQLITE_GENERAL_NS_SYNC_EVENT); + if (errCode != E_OK) { + LOGE("Register single version sync event failed:%d!", errCode); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_PUT_EVENT); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + return errCode; + } + + errCode = RegisterNotificationEventType(SQLITE_GENERAL_CONFLICT_EVENT); + if (errCode != E_OK) { + LOGE("Register single version sync event failed:%d!", errCode); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_SYNC_EVENT); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_PUT_EVENT); + UnRegisterNotificationEventType(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT); + return errCode; + } + + notificationEventsRegistered_ = true; + notificationConflictEventsRegistered_ = true; + return E_OK; +} + +void SQLiteSingleVerNaturalStore::ReleaseResources() +{ + 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:%llu", 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(std::vector &dataItems, const DeviceInfo &deviceInfo, + bool checkValueContent) +{ + // Sync procedure does not care readOnly Flag + CHECK_STORAGE_ENGINE; + 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); + } + + if (IsExtendedCacheDBMode()) { + errCode = SaveSyncDataToCacheDB(dataItems, deviceInfo); + } else { + errCode = SaveSyncDataToMain(dataItems, deviceInfo); + } + if (errCode != E_OK) { + LOGE("[SingleVerNStore] SaveSyncDataItems failed:%d", errCode); + } + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveSyncDataToMain(std::vector &dataItems, const DeviceInfo &deviceInfo) +{ + int errCode = E_OK; + LOGD("[SQLiteSingleVerNaturalStore::SaveSyncData] Get write handle."); + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + auto *committedData = new (std::nothrow) SingleVerNaturalStoreCommitNotifyData; + if (committedData == nullptr) { + LOGE("[SingleVerNStore] Failed to alloc single version notify data"); + ReleaseHandle(handle); + return -E_OUT_OF_MEMORY; + } + + InitConflictNotifiedFlag(committedData); + TimeStamp maxTimestamp = 0; + bool isNeedCommit = false; + errCode = SaveSyncItems(handle, dataItems, deviceInfo, maxTimestamp, committedData); + if (errCode == E_OK) { + isNeedCommit = true; + (void)SetMaxTimeStamp(maxTimestamp); + } + + CommitAndReleaseNotifyData(committedData, isNeedCommit, SQLITE_GENERAL_NS_SYNC_EVENT); + ReleaseHandle(handle); + return errCode; +} + +int SQLiteSingleVerNaturalStore::SaveSyncDataToCacheDB(std::vector &dataItems, const DeviceInfo &deviceInfo) +{ + int errCode = E_OK; + SQLiteSingleVerStorageExecutor *handle = GetHandle(true, errCode); + if (handle == nullptr) { + return errCode; + } + + TimeStamp maxTimestamp = 0; + errCode = SaveSyncItemsInCacheMode(handle, dataItems, deviceInfo, maxTimestamp); + if (errCode != E_OK) { + LOGE("[SingleVerNStore] Failed to save sync data in cache mode, err : %d", errCode); + } else { + (void)SetMaxTimeStamp(maxTimestamp); + } + 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(); + 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) +{ + CHECK_STORAGE_ENGINE; + 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) +{ + CHECK_STORAGE_ENGINE; + 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(); + 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); +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) const +{ + const SchemaObject &localSchema = MyProp().GetSchemaConstRef(); + if (!localSchema.IsSchemaValid() || schema.empty()) { + // 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 +{ + switch (observerType) { + case static_cast(SQLITE_GENERAL_NS_PUT_EVENT): + type = OBSERVER_SINGLE_VERSION_NS_PUT_EVENT; + return E_OK; + case static_cast(SQLITE_GENERAL_NS_SYNC_EVENT): + type = OBSERVER_SINGLE_VERSION_NS_SYNC_EVENT; + return E_OK; + case static_cast(SQLITE_GENERAL_NS_LOCAL_PUT_EVENT): + type = OBSERVER_SINGLE_VERSION_NS_LOCAL_EVENT; + return E_OK; + case static_cast(SQLITE_GENERAL_CONFLICT_EVENT): + type = OBSERVER_SINGLE_VERSION_NS_CONFLICT_EVENT; + return E_OK; + default: + return -E_NOT_SUPPORT; + } +} + +int SQLiteSingleVerNaturalStore::TransConflictTypeToRegisterFunctionType( + int conflictType, RegisterFuncType &type) const +{ + switch (conflictType) { + case static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ONLY): + type = CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ONLY; + return E_OK; + case static_cast(SQLITE_GENERAL_NS_FOREIGN_KEY_ORIG): + type = CONFLICT_SINGLE_VERSION_NS_FOREIGN_KEY_ORIG; + return E_OK; + case static_cast(SQLITE_GENERAL_NS_NATIVE_ALL): + type = CONFLICT_SINGLE_VERSION_NS_NATIVE_ALL; + return E_OK; + default: + return -E_NOT_SUPPORT; + } +} + +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][GetSchema] Get schema from db failed or no schema=%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); + + std::string currentDir = storeDir + DBConstant::MAINDB_DIR + "/"; + std::string currentOnlyDir = storeOnlyDir + DBConstant::MAINDB_DIR + "/"; + int removeMainErrCode = KvDBUtils::RemoveKvDB(currentDir, currentOnlyDir, DBConstant::SINGLE_VER_DATA_STORE); + if (removeMainErrCode != -E_NOT_FOUND && removeMainErrCode != E_OK) { + return removeMainErrCode; + } + currentDir = storeDir + DBConstant::METADB_DIR + "/"; + currentOnlyDir = storeOnlyDir + DBConstant::METADB_DIR + "/"; + int removeMetaErrCode = KvDBUtils::RemoveKvDB(currentDir, currentOnlyDir, DBConstant::SINGLE_VER_META_STORE); + if (removeMetaErrCode != -E_NOT_FOUND && removeMetaErrCode != E_OK) { + return removeMetaErrCode; + } + currentDir = storeDir + DBConstant::CACHEDB_DIR + "/"; + currentOnlyDir = storeOnlyDir + DBConstant::CACHEDB_DIR + "/"; + int removeCacheErrCode = KvDBUtils::RemoveKvDB(currentDir, currentOnlyDir, DBConstant::SINGLE_VER_CACHE_STORE); + if (removeCacheErrCode != -E_NOT_FOUND && removeCacheErrCode != E_OK) { + return removeCacheErrCode; + } + + // Signed numbers can not use bit operations + if (removeMainErrCode == -E_NOT_FOUND && removeMetaErrCode == -E_NOT_FOUND && removeCacheErrCode == -E_NOT_FOUND) { + 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); + 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_) { + auto identifier = GetMyProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + lifeCycleNotifier_(identifier); + } + return 0; + }, + [this]() { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this]() { + RefObject::DecObjRef(this); + }); + if (errCode != E_OK) { + LOGE("SQLiteSingleVerNaturalStore timer finalizer ScheduleTask, errCode %d", errCode); + } + }, + 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) { + EngineState state = storageEngine_->GetEngineState(); + if (state == EngineState::MIGRATING || state == EngineState::ENGINE_BUSY) { + 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) != 0) { + // 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, + 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); + 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); +} + +DEFINE_OBJECT_TAG_FACILITIES(SQLiteSingleVerNaturalStore) +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h new file mode 100755 index 000000000..71b488d5b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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" + +namespace DistributedDB { +struct ContinueTokenStruct { + /* + * function: Check the magic number at the beginning and end of the ContinueTokenStruct. + * 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 + { + return ((magicBegin_ == MAGIC_BEGIN) && (magicEnd_ == MAGIC_END)); + } + TimeStamp GetBeginTimeStamp() const + { + return begin_; + } + void SetBeginTimeStamp(TimeStamp begin) + { + begin_ = begin; + } + TimeStamp GetEndTimeStamp() const + { + return end_; + } + void SetEndTimeStamp(TimeStamp end) + { + end_ = end; + } + +private: + 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; + TimeStamp begin_ = 0; + TimeStamp end_ = 0; + unsigned int magicEnd_ = MAGIC_END; +}; + +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 interface for syncer. + IKvDBSyncInterface *GetSyncInterface() override; + + int GetMetaData(const Key &key, Value &value) const override; + + int PutMetaData(const Key &key, const Value &value) 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 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 PutSyncData(std::vector &dataItems, const std::string &deviceName) override; + + int PutSyncData(const std::vector &entries, const std::string &deviceName) override; + + void ReleaseKvEntry(const SingleVerKvEntry *entry) 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) 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 IsReadable() 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; + +private: + int CheckDatabaseRecovery(const KvDBProperties &kvDBProp); + + void CommitAndReleaseNotifyData(SingleVerNaturalStoreCommitNotifyData *&committedData, + bool isNeedCommit, int eventType); + + int RegisterNotification(); + + void ReleaseResources(); + + void InitCurrentMaxStamp(); + + int SaveSyncDataItems(std::vector &dataItems, const DeviceInfo &deviceInfo, bool checkValueContent); + + int GetData(const SQLiteStorageExecutor* handle, const std::string &sql, const Key &key, Value &value) const; + + 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(std::vector &dataItems, const DeviceInfo &deviceInfo); + + int SaveSyncDataToCacheDB(std::vector &dataItems, const DeviceInfo &deviceInfo); + + int SaveSyncItemsInCacheMode(SQLiteSingleVerStorageExecutor *handle, + std::vector &dataItems, const DeviceInfo &deviceInfo, TimeStamp &maxTimestamp) const; + + int ClearIncompleteDatabase(const KvDBProperties &kvDBPro) const; + + 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_; +}; +} +#endif diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp new file mode 100755 index 000000000..cf37553e4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.cpp @@ -0,0 +1,1693 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "log_print.h" +#include "kvdb_pragma.h" +#include "sqlite_single_ver_natural_store.h" +#include "kvdb_observer_handle.h" +#include "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; + } + + { + // need to check if the transaction started + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + TimeStamp recordTimeStamp; + return writeHandle_->GetKvData(dataType, key, value, recordTimeStamp); + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + return errCode; + } + + TimeStamp timeStamp; + errCode = handle->GetKvData(dataType, key, value, timeStamp); + ReleaseExecutor(handle); + 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; + } + + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + return writeHandle_->GetEntries(type, keyPrefix, entries); + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + LOGE("[Connection]::[GetEntries] Get executor failed, errCode = [%d]", errCode); + return errCode; + } + + errCode = handle->GetEntries(type, keyPrefix, entries); + ReleaseExecutor(handle); + 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); + } + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + return writeHandle_->GetEntries(queryObj, entries); + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + return errCode; + } + + errCode = handle->GetEntries(queryObj, entries); + ReleaseExecutor(handle); + 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); + } + { + std::lock_guard lock(transactionMutex_); + if (writeHandle_ != nullptr) { + LOGD("Transaction started already."); + return writeHandle_->GetCount(queryObj, count); + } + } + + SQLiteSingleVerStorageExecutor *handle = GetExecutor(false, errCode); + if (handle == nullptr) { + return errCode; + } + errCode = handle->GetCount(queryObj, count); + ReleaseExecutor(handle); + 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; + } + if (static_cast(parameter)->deviceID == "") { + return -E_INVALID_ARGS; + } + static_cast(parameter)->deviceIdentifier = + DBCommon::TransferHashString(static_cast(parameter)->deviceID); + break; + } + 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)); + 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::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) +{ + 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; + } + } + + if ((transactionEntrySize_ + entries.size()) > DBConstant::MAX_TRANSACTION_ENTRY_SIZE) { + 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; + } + } + return errCode; +} + +int SQLiteSingleVerNaturalStoreConnection::DeleteBatchInner(const IOption &option, const std::vector &keys) +{ + 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; + } + } + + if ((transactionEntrySize_ + keys.size()) > DBConstant::MAX_TRANSACTION_ENTRY_SIZE) { + 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; + } + } + 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 %llu", 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, ""}; + errCode = writeHandle_->SaveSyncDataItemInCacheMode(dataItem, deviceInfo, maxTimestamp, recordVersion); + 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; + } + errCode = handle->StartTransaction(TransactType::DEFERRED); + if (errCode != E_OK) { + ReleaseExecutor(handle); + return errCode; + } + + LOGD("[SingleVerConnection] Start transaction finish in cache mode."); + 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 (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; + } + + LOGD("[SingleVerConnection] Start transaction finish."); + 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); + } + LOGD("[SingleVer][InitConflictNotifiedFlag] conflictFlag Flag: %u", conflictFlag); + committedData_->SetConflictedNotifiedFlag(static_cast(conflictFlag)); +} + +int SQLiteSingleVerNaturalStoreConnection::CommitInner() +{ + bool isCacheOrMigrating = IsExtendedCacheDBMode(); + + int errCode = writeHandle_->Commit(); + ReleaseExecutor(writeHandle_); + LOGD("Commit transaction finish."); + 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(); + LOGD("Rollback transaction finish."); + 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] Kvstore is null, get executor failed! errCode = [%d]", errCode); + return nullptr; + } + return naturalStore->GetHandle(isWrite, errCode); +} + +bool SQLiteSingleVerNaturalStoreConnection::IsCacheDBMode() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] natural store is null in IsCacheDBMode."); + return false; + } + return naturalStore->IsCacheDBMode(); +} + +bool SQLiteSingleVerNaturalStoreConnection::IsExtendedCacheDBMode() const +{ + SQLiteSingleVerNaturalStore *naturalStore = GetDB(); + if (naturalStore == nullptr) { + LOGE("[SingleVerConnection] natural store is nullptr in IsExtendedCacheDBMode."); + 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); +} + +DEFINE_OBJECT_TAG_FACILITIES(SQLiteSingleVerNaturalStoreConnection) +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.h new file mode 100755 index 000000000..8a619ff69 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_natural_store_connection.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 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; + +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; + + 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.cpp new file mode 100755 index 000000000..b24863a85 --- /dev/null +++ b/services/distributeddataservice/libs/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.", 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.", 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.", type_, 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h new file mode 100755 index 000000000..5fbe63954 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_result_set.h @@ -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. + */ + +#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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.cpp new file mode 100755 index 000000000..4aaaf25ce --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_database_upgrader.h new file mode 100755 index 000000000..0f3d41a9d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_schema_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 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp new file mode 100755 index 000000000..61bfe157b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.cpp @@ -0,0 +1,1069 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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[%llu], minVer[%llu] maxVer[%llu]", 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::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[%llu]", GetCacheRecordVersion() - 1); + uint64_t curMigrateVer = 0; // The migration process is asynchronous and continuous + NotifyMigrateSyncData syncData; + // 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[%llu] 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) +{ + LOGD("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 [%d]", 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; + } + + bool isNeedTriggerSync = false; + errCode = InitExecuteMigrate(handle, preState); + if (errCode != E_OK) { + LOGE("Init migrate data fail, errCode = [%d]", errCode); + goto END; + } + + LOGD("[SqlSingleVerEngine] Current enginState [%d] executorState [%d], begin to executing singleVer db migrate!", + preState, executorState_); + // has been attach, Mark start of migration and it can migrating 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); + 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); + } + // 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); + if (executor == nullptr) { + return executor; + } + executor->SetConflictResolvePolicy(option_.conflictReslovePolicy); + return executor; +} + +int SQLiteSingleVerStorageEngine::TryToOpenMainDatabase(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; + } + + int errCode = SQLiteUtils::OpenDatabase(option_, 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; + } + + // 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); + executorState_ = ExecutorState::MAINDB; + 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(dbHandle); + LOGD("Finish to open the main database, write[%d], label[%d], flag[%d], errCode[%d]", + isWrite, secOpt.securityLabel, secOpt.securityFlag, 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 == true) { + 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); + } + } +} + +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 +{ + auto &isRemote = syncData.isRemote; + 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; +} +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h new file mode 100755 index 000000000..87a7bbdaa --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_engine.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + } + +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(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; + + mutable std::mutex migrateLock_; + std::atomic cacheRecordVersion_; + ExecutorState executorState_; + bool isCorrupted_; + bool isNeedUpdateSecOpt_; // update the option_ +}; +} // namespace DistributedDB + +#endif // SQLITE_SINGLE_VER_STORAGE_ENGINE_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp new file mode 100755 index 000000000..892bd7edf --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.cpp @@ -0,0 +1,1906 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { +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); + } +} + +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 &timeStamp) 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_ == true) { + 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)); + LOGD("[SingleVerExe][GetKv] Timestamp from local is %llu", timeStamp); + } else if (type == SingleVerDataType::SYNC_TYPE) { + timeStamp = static_cast(sqlite3_column_int64(statement, GET_KV_RES_SYNC_TIME_INDEX)); + LOGD("[SingleVerExe][GetKv] Timestamp from sync is %llu", timeStamp); + } + +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 +{ + std::string sql; + int errCode = queryObj.GetQuerySql(sql); + if (errCode != E_OK) { + return errCode; + } + sqlite3_stmt *statement = nullptr; + errCode = queryObj.GetQuerySqlStatement(dbHandle_, sql, statement); + if (errCode != E_OK) { + goto END; + } + errCode = StepForResultEntries(statement, entries); +END: + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::GetCount(QueryObject &queryObj, int &count) const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + + std::string countSql; + int errCode = queryObj.GetCountQuerySql(countSql); + if (errCode != E_OK) { + return errCode; + } + + if (!queryObj.IsCountValid()) { + LOGE("GetCount no need limit or orderby"); + return -E_INVALID_QUERY_FORMAT; + } + + sqlite3_stmt *countStatement = nullptr; + // get statement for count + errCode = queryObj.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) { + goto ERROR; + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + maxStamp = static_cast(sqlite3_column_int64(statement, 0)); // get the first column + LOGD("Max time stamp is %llu", maxStamp); + } + +ERROR: + SQLiteUtils::ResetStatement(statement, true, errCode); +} + +int SQLiteSingleVerStorageExecutor::PrepareForSyncDataByTime(TimeStamp begin, TimeStamp end, + sqlite3_stmt *&statement) const +{ + if (dbHandle_ == nullptr) { + return -E_INVALID_DB; + } + int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_ENTRIES_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); + if (errCode != E_OK) { + goto ERROR; + } + +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(); + } + } +} + +int SQLiteSingleVerStorageExecutor::GetSyncDataByTimestamp(std::vector &dataItems, size_t appenedLength, + TimeStamp begin, TimeStamp end, const DataSizeSpecInfo &dataSizeInfo) const +{ + size_t dataTotalSize = 0; + sqlite3_stmt *statement = nullptr; + int errCode = PrepareForSyncDataByTime(begin, end, statement); + if (errCode != E_OK) { + return errCode; + } + + dataItems.clear(); + do { + DataItem dataItem; + errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = GetDataItemForSync(statement, dataItem); + } else { + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + LOGD("Get sync data finished, number: %zu, size: %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, appenedLength); + if ((dataTotalSize > dataSizeInfo.blockSize && !dataItems.empty()) || + dataItems.size() >= dataSizeInfo.packetSize) { + errCode = -E_UNFINISHED; + break; + } else { + dataItems.push_back(std::move(dataItem)); + } + } while (true); + + SQLiteUtils::ResetStatement(statement, true, errCode); + return CheckCorruptedStatus(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; + if (!queryObj.IsValid(errCode)) { + LOGE("[SqlSinExe][OpenResSetRowId][Query] Not Valid, errCode=%d", errCode); + return errCode; + } + std::string selectSql; + errCode = queryObj.GetQuerySql(selectSql, true); // only rowid sql + if (errCode != E_OK) { + LOGE("[SqlSinExe][OpenResSetRowId][Query] Get SQL fail, errCode=%d", errCode); + return errCode; + } + errCode = queryObj.GetQuerySqlStatement(dbHandle_, selectSql, 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 = E_OK; + SQLiteUtils::ResetStatement(getResultRowIdStatement_, false, errCode); + if (errCode != E_OK) { + LOGE("Reset result set rowid statement of keyPrefix error:%d", errCode); + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_SYNC_ROWID_PREFIX_SQL, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("Reset result set rowid statement of keyPrefix error:%d", errCode); + 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; + bool isValid = queryObj.IsValid(errCode); + if (!isValid) { + return errCode; + } + std::string sql; + errCode = queryObj.GetQuerySql(sql, true); // only rowid sql + if (errCode != E_OK) { + return errCode; + } + + SQLiteUtils::ResetStatement(getResultRowIdStatement_, false, errCode); + if (errCode != E_OK) { + LOGE("Reset result set rowid statement of query error:%d", errCode); + // Finish current statement and remade one + SQLiteUtils::ResetStatement(getResultRowIdStatement_, true, errCode); + errCode = SQLiteUtils::GetStatement(dbHandle_, sql, getResultRowIdStatement_); + if (errCode != E_OK) { + LOGE("Reget result set rowid statement of query error:%d", errCode); + 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 = queryObj.GetQuerySqlStatement(dbHandle_, sql, 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; + } + return E_OK; +} + +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; + } + return E_OK; +} + +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) { + isTransactionOpen_ = false; + } + return CheckCorruptedStatus(errCode); +} + +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 &timeStamp) 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 &writeSql, + SaveRecordStatements &statements) const +{ + int errCode = SQLiteUtils::GetStatement(dbHandle_, readSql, statements.queryStatement); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::GetStatement(dbHandle_, writeSql, statements.putStatement); + if (errCode != E_OK) { + SQLiteUtils::ResetStatement(statements.queryStatement, true, errCode); + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::PrepareForSavingData(SingleVerDataType type) +{ + int errCode = -E_NOT_SUPPORT; + if (type == SingleVerDataType::LOCAL_TYPE) { + errCode = PrepareForSavingData(SELECT_LOCAL_HASH_SQL, INSERT_LOCAL_SQL, saveLocalStatements_); + } else if (type == SingleVerDataType::SYNC_TYPE) { + errCode = PrepareForSavingData(SELECT_SYNC_HASH_SQL, INSERT_SYNC_SQL, saveSyncStatements_); + } + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::ResetForSavingData(SingleVerDataType type) +{ + int errCode = E_OK; + if (type == SingleVerDataType::LOCAL_TYPE) { + SQLiteUtils::ResetStatement(saveLocalStatements_.putStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.queryStatement, false, errCode); + } else if (type == SingleVerDataType::SYNC_TYPE) { + SQLiteUtils::ResetStatement(saveSyncStatements_.putStatement, 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) +{ + auto statement = saveSyncStatements_.putStatement; + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + std::string devName = DBCommon::TransferHashString(deviceName); + int errCode = BindSavedSyncData(statement, dataItem, hashKey, {origDev, devName}); + 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) +{ + DataOperStatus status; + status.isDeleted = ((dataItem.flag & DataItem::DELETE_FLAG) != 0); + 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) { + if ((!deviceName.empty()) && (itemGet.dev == deviceName)) { + LOGI("Force overwrite the data:%llu vs %llu", 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_.putStatement, 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) +{ + // 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_IGNOR_DATA; + } + return ResetSaveSyncStatements(-E_IGNOR_DATA); + } + + notify.dataStatus = JudgeSyncSaveType(dataItem, notify.getData, deviceInfo.deviceName, isHashKeyExisted); + 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) +{ + NotifyConflictAndObserverData notify = { + .committedData = committedData + }; + + int errCode = PrepareForNotifyConflictAndObserver(dataItem, deviceInfo, notify); + if (errCode != E_OK) { + if (errCode == -E_IGNOR_DATA) { + errCode = E_OK; + } + return errCode; + } + + PutConflictData(dataItem, notify.getData, deviceInfo, notify.dataStatus, committedData); + if (notify.dataStatus.isDefeated == true) { + LOGD("Data status is defeated:%d", errCode); + return ResetSaveSyncStatements(errCode); + } + + std::string origDev = GetOriginDevName(dataItem, notify.getData.origDev); + errCode = SaveSyncDataToDatabase(dataItem, notify.hashKey, origDev, deviceInfo.deviceName); + 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; + int errCode = -E_BASE; + if (attachMetaMode_ == true) { + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_ATTACH_ALL_META_KEYS, statement); + } else { + errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_ALL_META_KEYS, 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 hashstring. + // Therefore no need to transfer hashstring 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); + goto ERROR; + } + + errCode = GetAllEntries(statement, entries); +ERROR: + 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, int beginIndex) +{ + int errCode = SQLiteUtils::BindBlobToStatement(statement, beginIndex + BIND_SYNC_HASH_KEY_INDEX, 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, beginIndex + BIND_SYNC_KEY_INDEX, -1)); + } else { + errCode = SQLiteUtils::BindBlobToStatement(statement, beginIndex + 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, beginIndex + 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, beginIndex + BIND_SYNC_STAMP_INDEX, dataItem.timeStamp); + if (errCode != E_OK) { + LOGE("Bind saved sync data stamp failed:%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindInt64ToStatement(statement, beginIndex + BIND_SYNC_W_TIME_INDEX, + dataItem.writeTimeStamp); + LOGD("Write timestamp:%llu timestamp:%llu, %llu", 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 == true))) { + return; + } + + Key origKey; + if ((itemPut.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + 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) { + 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 &timeStamp) +{ + int errCode = GetKvData(SingleVerDataType::LOCAL_TYPE, key, value, timeStamp); + if (errCode != E_OK) { + return CheckCorruptedStatus(errCode); + } + + return DeleteLocalDataInner(committedData, key, value); +} + +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 beginIndex) +{ + int errCode = SQLiteUtils::BindInt64ToStatement(statement, beginIndex + 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, beginIndex + 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, beginIndex + 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::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::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; + } + + std::string countSql; + int errCode = queryObj.GetCountQuerySql(countSql); + if (errCode != E_OK) { + return errCode; + } + + errCode = queryObj.GetCountSqlStatement(dbHandle_, countSql, countStmt); + if (errCode != E_OK) { + LOGE("Get count bind statement error:%d", errCode); + SQLiteUtils::ResetStatement(countStmt, true, errCode); + return errCode; + } + return errCode; +} + +int SQLiteSingleVerStorageExecutor::InitResultSetContent(QueryObject &queryObj) +{ + std::string selectSql; + int errCode = queryObj.GetQuerySql(selectSql, true); // only rowid sql + if (errCode != E_OK) { + return errCode; + } + // bind statement for result set + errCode = queryObj.GetQuerySqlStatement(dbHandle_, selectSql, 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; + bool isValid = queryObj.IsValid(errCode); + if (!isValid) { + return errCode; + } + + 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) { + return E_OK; + } + 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; +} + +void SQLiteSingleVerStorageExecutor::FinalizeAllStatements() +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(saveLocalStatements_.putStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize saveLocal put statements failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(saveLocalStatements_.queryStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize saveLocal query statement failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(saveSyncStatements_.putStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize saveSync put statement failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(saveSyncStatements_.queryStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize saveSync query 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); + } + + SQLiteUtils::ResetStatement(migrateSyncStatements_.putStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize migrateSync put statements failed, error: %d", errCode); + } + + SQLiteUtils::ResetStatement(migrateSyncStatements_.queryStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Finalize migrateSync query statement 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; + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h new file mode 100755 index 000000000..a1ea09068 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + 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 &timeStamp) 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); + + int DeleteLocalKvData(const Key &key, SingleVerNaturalStoreCommitNotifyData *committedData, Value &value, + TimeStamp &timeStamp); + + 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 appenedLength, 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 &timeStamp) 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); + + 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); + +private: + struct SaveRecordStatements { + sqlite3_stmt *queryStatement = nullptr; + sqlite3_stmt *putStatement = nullptr; + }; + + 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, int beginIndex = 0); + + static int BindDevForSavedSyncData(sqlite3_stmt *statement, const DataItem &dataItem, const std::string &origDev, + const std::string &deviceName, int beginIndex = 0); + + static int GetDataItemForSync(sqlite3_stmt *statement, DataItem &dataItem); + + static size_t GetDataItemSerialSize(const DataItem &item, size_t appendLen); + + 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); + + 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) 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); + + 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 &writeSql, + 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; + int PutMigratingDataToMain(const std::vector &dataItems) 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); + // Put observer and conflict data into commit notify when migrating cacheDB. + int PutIntoConflictAndCommitForMigrateCache(DataItem &dataItem, const DeviceInfo &deviceInfo, + NotifyConflictAndObserverData ¬ify); + // Notify when migrating cacheDB. + int NotifyForMigrateCacheDB(std::vector &dataItems, 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; + + 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp new file mode 100755 index 000000000..ea9a1a1d3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_cache.cpp @@ -0,0 +1,874 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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); + errCode = PrepareForSavingData(SELECT_CACHE_LOCAL_HASH_SQL, insertLocalSql, 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 selectSyncHashSql = ((executorState_ == ExecutorState::MAIN_ATTACH_CACHE) ? + SELECT_CACHE_SYNC_HASH_SQL_FROM_MAINHANDLE : SELECT_CACHE_SYNC_HASH_SQL); + errCode = PrepareForSavingData(selectSyncHashSql, insertSyncSql, 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_.putStatement, false, errCode); + SQLiteUtils::ResetStatement(saveLocalStatements_.queryStatement, false, errCode); + } else if (type == SingleVerDataType::SYNC_TYPE) { + SQLiteUtils::ResetStatement(saveSyncStatements_.putStatement, false, errCode); + SQLiteUtils::ResetStatement(saveSyncStatements_.queryStatement, false, errCode); + } + + return CheckCorruptedStatus(errCode); +} + +int SQLiteSingleVerStorageExecutor::ResetForMigrateCacheData() +{ + int errCode = E_OK; + SQLiteUtils::ResetStatement(migrateSyncStatements_.putStatement, 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("[sinverExecutor][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::PutMigratingDataToMain(const std::vector &dataItems) const +{ + std::string sql; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + sql = MIGRATE_PUT_DATA_TO_MAINDB_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + sql = MIGRATE_PUT_DATA_TO_MAINDB_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 put migrating-data to main! errCode = [%d]", errCode); + return CheckCorruptedStatus(errCode); + } + + 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) { + errCode = MigrateRmDevData(dataItem); + LOGI("[PutMigratingDataToMain]Execute remove devices data! errCode = [%d]", errCode); + if (errCode != E_OK) { + break; + } + continue; + } + SyncDataDevices devices{ dataItem.origDev, dataItem.dev }; + errCode = BindSavedSyncData(statement, dataItem, dataItem.hashKey, devices); + 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!"); + break; + } + SQLiteUtils::ResetStatement(statement, false, errCode); + } +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[%d], executorState[%d]", engineState, 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::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 timestap for mingrate + errCode = ProcessTimeStampForSyncDataInCacheDB(dataItems); + if (errCode != E_OK) { + LOGE("Chang the time stamp for migrate failed! errCode = [%d]", errCode); + goto END; + } + + // observer and conflictdata + errCode = NotifyForMigrateCacheDB(dataItems, syncData); + if (errCode != E_OK) { + LOGE("Notify cacheDb to mainDb failed! errCode = [%d]", errCode); + goto END; + } + + // Put this version into main db + errCode = PutMigratingDataToMain(dataItems); + if (errCode != E_OK) { + LOGE("Put migrating data to main db failed! errCode = [%d]", errCode); + 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:%llu timestamp%llu, %llu, version %llu", 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::SaveSyncDataItemInCacheMode(DataItem &dataItem, + const DeviceInfo &deviceInfo, TimeStamp &maxStamp, uint64_t recordVersion) +{ + Key hashKey; + if ((dataItem.flag & DataItem::DELETE_FLAG) == DataItem::DELETE_FLAG) { + hashKey = dataItem.key; + } else { + int errCode = DBCommon::CalcValueHash(dataItem.key, hashKey); + if (errCode != E_OK) { + 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; + int 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_.putStatement; + 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) +{ + int errCode = PrepareForNotifyConflictAndObserver(dataItem, deviceInfo, notify); + if (errCode != E_OK) { + errCode = (errCode == -E_NOT_FOUND ? E_OK : errCode); + if (errCode == -E_IGNOR_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 == true) { + 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 %llu", 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 %llu, max timestamp in mainDB is %llu. Time offset during migrating is %lld.", + minTimeInCache, maxTimeInMain, migrateTimeOffset_); + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::ProcessTimeStampForSyncDataInCacheDB(std::vector &dataItems) +{ + if (dataItems.size() == 0) { + 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::NotifyForMigrateCacheDB(std::vector &dataItems, + NotifyMigrateSyncData &syncData) +{ + auto &isRemote = syncData.isRemote; + auto &isRemoveDeviceData = syncData.isRemoveDeviceData; + auto &committedData = syncData.committedData; + + isRemote = ((dataItems[0].flag & DataItem::LOCAL_FLAG) == 0); + isRemoveDeviceData = (dataItems[0].flag & DataItem::REMOVE_DEVICE_DATA_FLAG) != 0 || + (dataItems[0].flag & DataItem::REMOVE_DEVICE_DATA_NOTIFY_FLAG) != 0; + + for (auto iter = dataItems.begin(); iter != dataItems.end();) { + auto &item = *iter; + // Remove device data owns one version itself. + // Get entry here. Prepare notify data in storageEngine. + if (isRemoveDeviceData) { + return GetEntriesForNotifyRemoveDevData(item, syncData.entries); + } + + // Put or delete. Prepare notify data here. + DeviceInfo deviceInfo = {item.dev.empty(), item.dev}; + NotifyConflictAndObserverData notify; + notify.committedData = committedData; + int errCode = PutIntoConflictAndCommitForMigrateCache(item, deviceInfo, notify); + if (errCode != E_OK) { + ResetForMigrateCacheData(); + LOGE("PutIntoConflictAndCommitForMigrateCache failed, errCode = %d", errCode); + return errCode; + } + errCode = ResetForMigrateCacheData(); + if (errCode != E_OK) { + LOGE("ResetForMigrateCacheData failed, errCode = %d", errCode); + return errCode; + } + // after solving conflict, the item should not be saved into mainDB + if (notify.dataStatus.isDefeated == true) { + iter = dataItems.erase(iter); + continue; + } + iter++; + } + return E_OK; +} + +int SQLiteSingleVerStorageExecutor::InitMigrateData() +{ + // Sync_data already in migrating. Need not to init data. + if (isSyncMigrating_) { + return E_OK; + } + ClearMigrateData(); + std::string querySQL; + std::string putSQL; + if (executorState_ == ExecutorState::MAIN_ATTACH_CACHE) { + querySQL = SELECT_SYNC_HASH_SQL; + putSQL = MIGRATE_PUT_DATA_TO_MAINDB_FROM_MAINHANDLE; + } else if (executorState_ == ExecutorState::CACHE_ATTACH_MAIN) { + querySQL = SELECT_MAIN_SYNC_HASH_SQL_FROM_CACHEHANDLE; + putSQL = MIGRATE_PUT_DATA_TO_MAINDB_FROM_CACHEHANDLE; + } else { + LOGE("[InitMigrateData] executor in an error state[%d]!", executorState_); + return -E_INVALID_DB; + } + int errCode = PrepareForSavingData(querySQL, putSQL, 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 = E_OK; + SQLiteUtils::ResetStatement(migrateSyncStatements_.putStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Reset migrateSyncStatements_.putStatement fail, errCode = %d", errCode); + } + errCode = E_OK; + SQLiteUtils::ResetStatement(migrateSyncStatements_.queryStatement, true, errCode); + if (errCode != E_OK) { + LOGE("Reset migrateSyncStatements_.queryStatement fail, errCode = %d", errCode); + } + + isSyncMigrating_ = false; +} + +int SQLiteSingleVerStorageExecutor::GetMaxTimeStampDuringMigrating(TimeStamp &maxTimeStamp) const +{ + if (maxTimeStampInMainDB_ == 0) { + return -E_NOT_INIT; + } + maxTimeStamp = maxTimeStampInMainDB_; + return E_OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h new file mode 100755 index 000000000..ff7b927a0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_single_ver_storage_executor_sql.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 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 OR REPLACE INTO sync_data VALUES(?,?,?,?,?,?,?,?);"; + + 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 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_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_PUT_DATA_TO_MAINDB_FROM_CACHEHANDLE = + "INSERT OR REPLACE INTO maindb.sync_data VALUES(?,?,?,?,?,?,?,?);"; + const std::string MIGRATE_PUT_DATA_TO_MAINDB_FROM_MAINHANDLE = + "INSERT OR REPLACE INTO sync_data VALUES(?,?,?,?,?,?,?,?);"; + + 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 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; + + // 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp new file mode 100755 index 000000000..b3ce9d9ff --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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. + 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; + } + 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 == MAINDB) || (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(); + } + std::string schemaStr = kvDBProp.GetSchema().ToSchemaString(); + 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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_engine.h new file mode 100755 index 000000000..068448da6 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.cpp new file mode 100755 index 000000000..9991bbea8 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_storage_executor.h new file mode 100755 index 000000000..10abd5db2 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp new file mode 100755 index 000000000..e6a5aaf49 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp @@ -0,0 +1,1402 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "db_common.h" +#include "log_print.h" +#include "value_object.h" +#include "schema_utils.h" +#include "time_helper.h" +#include "platform_specific.h" + +namespace DistributedDB { +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'"; + bool g_configLog = false; + std::mutex g_logMutex; + + void 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); + } + } +} + +int SQLiteUtils::CreateDataBase(const OpenDbProperties &properties, sqlite3 *&dbTemp) +{ + 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 {WAL_MODE_SQL, defaultAttachCipher, DEFAULT_ATTACH_KDF_ITER}; + + 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) +{ + { + // Only for register the sqlite3 log callback + std::lock_guard lock(g_logMutex); + if (!g_configLog) { + sqlite3_config(SQLITE_CONFIG_LOG, &SqliteLogCallback, &properties.createIfNecessary); + g_configLog = true; + } + } + sqlite3 *dbTemp = nullptr; + int errCode = CreateDataBase(properties, dbTemp); + 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); + } +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()) { + LOGI("[SQLiteUtil][Bind Text] Invalid value"); + return -E_INVALID_ARGS; + } + + 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) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + 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; + } + 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::AttachNewDatabase(sqlite3 *db, CipherType type, const CipherPassword &passwd, + 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(passwd.GetData()), + passwd.GetSize(), SQLITE_TRANSIENT); // 2nd para is password. + 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; +} + +#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) +{ + return -E_NOT_SUPPORT; +} + +int SQLiteUtils::Rekey(sqlite3 *db, const CipherPassword &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); + 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); + LOGD("[SqlUtil][GetVer] db version=%d", version); + } else { + LOGE("[SqlUtil][GetVer] Get db user_version failed."); + errCode = SQLiteUtils::MapSQLiteErrno(SQLITE_ERROR); + } + + 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) +{ + 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; +} +} + +int SQLiteUtils::RegisterFlatBufferFunction(sqlite3 *db, const std::string &inSchema) +{ + if (db == nullptr) { + 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; +} + +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 *inCahce) +{ + delete inCahce; + inCahce = 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() % 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 < 0) { + LOGE("Failed to get the db file size, errno:%d", errno); + if (errno == ENOENT) { + return -E_NOT_FOUND; + } + return -E_INVALID_DB; + } + + std::string shmFileName = dataDir + "-shm"; + uint64_t localshmFileSize = 0; + errCode = OS::CalFileSize(shmFileName, localshmFileSize); + if (errCode < 0) { + localshmFileSize = 0; + } + + std::string walFileName = dataDir + "-wal"; + uint64_t localWalFileSize = 0; + errCode = OS::CalFileSize(walFileName, localWalFileSize); + if (errCode < 0) { + 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) { + 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), ' '); + } + LOGD("#### %s", rowString.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); + 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 +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.h b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.h new file mode 100755 index 000000000..0ec724843 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sqlite/sqlite_utils.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sqlite_import.h" + +#include "types.h" +#include "db_types.h" +#include "schema_object.h" +#include "iprocess_system_api_adapter.h" + +namespace DistributedDB { +enum class TransactType { + DEFERRED, + IMMEDIATE, +}; + +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); + + // 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); + + static int GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &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 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 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 &passwd, + const std::string &attachDbAbsPath, const std::string &attachAsName = "backup"); + + static int CreateMetaDatabase(const std::string &metaDbPath); +private: + + static int CreateDataBase(const OpenDbProperties &properties, sqlite3 *&dbTemp); + + 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 GetSysCurrentTime(sqlite3_context *ctx, int argc, sqlite3_value **argv); + + static int SetDataBaseProperty(sqlite3 *db, const OpenDbProperties &properties, + const std::vector &sqls); + +#ifndef OMIT_ENCRYPT + static int SetCipherSettings(sqlite3 *db, CipherType type); + + static std::string GetCipherName(CipherType type); +#endif +}; +}; // namespace DistributedDB + +#endif // SQLITE_UTILS_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.cpp new file mode 100755 index 000000000..3939e4ced --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.cpp @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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), + 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[%d]", 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[%d], operType[%d], write[%d-%d-%d]", + result, perm_, 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[%d]", 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[%d], operType[%d], read[%d-%d-%d]", + result, perm_, 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("Set 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) { + 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); + std::string id = DBCommon::TransferStringToHex(identifier_); + LOGD("Get executor from [%.6s], write[%d], using[%d], idle[%d]", + id.c_str(), isWrite, usingList.size(), idleList.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); +} +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.h b/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine.h new file mode 100755 index 000000000..7d3ca0241 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/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 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; + +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::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/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine_manager.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine_manager.cpp new file mode 100755 index 000000000..c4116a16e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/storage_engine_manager.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_; +StorageEngineManager *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); + return 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() +{ + 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_->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/services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.cpp new file mode 100755 index 000000000..9b6a9faf7 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.h b/services/distributeddataservice/libs/distributeddb/storage/src/storage_executor.h new file mode 100755 index 000000000..9645cef7a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.cpp new file mode 100755 index 000000000..3ae8676f7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "log_print.h" +#include "parcel.h" + +namespace DistributedDB { +const EventType SyncAbleKvDB::REMOTE_PUSH_FINISHED = 1; + +SyncAbleKvDB::SyncAbleKvDB() + : started_(false), + remotePushNotifyChain_(nullptr) +{} + +SyncAbleKvDB::~SyncAbleKvDB() +{ + if (remotePushNotifyChain_ != nullptr) { + (void)remotePushNotifyChain_->UnRegisterEventType(REMOTE_PUSH_FINISHED); + KillAndDecObjRef(remotePushNotifyChain_); + remotePushNotifyChain_ = 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(); +} + +// Start a sync action. +int SyncAbleKvDB::Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait = false) +{ + if (!started_) { + StartSyncer(); + if (!started_) { + return -E_NOT_INIT; + } + } + return syncer_.Sync(devices, mode, onComplete, onFinalize, wait); +} + +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(int syncId) +{ + if (started_) { + syncer_.RemoveSyncOperation(syncId); + } +} + +// Start syncer +void SyncAbleKvDB::StartSyncer() +{ + IKvDBSyncInterface *syncInterface = GetSyncInterface(); + if (syncInterface == nullptr) { + LOGF("KvDB got null sync interface."); + return; + } + + int errCode = syncer_.Initialize(syncInterface); + if (errCode == E_OK) { + started_ = true; + } else { + LOGE("KvDB start syncer failed, err:'%d'.", errCode); + } +} + +// Stop syncer +void SyncAbleKvDB::StopSyncer() +{ + if (started_) { + syncer_.Close(); + started_ = false; + } +} + +// Get The current virtual timestamp +uint64_t SyncAbleKvDB::GetTimeStamp() +{ + if (!started_) { + 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) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.GetLocalIdentity(outTarget); +} + +int SyncAbleKvDB::SetStaleDataWipePolicy(WipePolicy policy) +{ + if (!started_) { + StartSyncer(); + } + return syncer_.SetStaleDataWipePolicy(policy); +} + +NotificationChain::Listener *SyncAbleKvDB::AddRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier, + int errCode) +{ + { + std::lock_guard lock(remotePushNotifyChainLock_); + if (remotePushNotifyChain_ == nullptr) { + remotePushNotifyChain_ = new (std::nothrow) NotificationChain; + if (remotePushNotifyChain_ == nullptr) { + errCode = -E_OUT_OF_MEMORY; + return nullptr; + } + + errCode = remotePushNotifyChain_->RegisterEventType(REMOTE_PUSH_FINISHED); + if (errCode != E_OK) { + LOGE("[SyncAbleKvDB] Register remote push finished event type failed! err %d", errCode); + KillAndDecObjRef(remotePushNotifyChain_); + remotePushNotifyChain_ = nullptr; + return nullptr; + } + } + } + + NotificationChain::Listener *listener = + remotePushNotifyChain_->RegisterListener(REMOTE_PUSH_FINISHED, + [notifier](void *arg) { + 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 +{ + if (remotePushNotifyChain_ != nullptr) { + RemotePushNotifyInfo info; + info.deviceId = targetId; + remotePushNotifyChain_->NotifyEvent(REMOTE_PUSH_FINISHED, static_cast(&info)); + } +} +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.h b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.h new file mode 100755 index 000000000..1b8116245 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb.h @@ -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. + */ + +#ifndef SYNC_ABLE_KVDB_H +#define SYNC_ABLE_KVDB_H + +#include "generic_kvdb.h" +#include "sync_able_kvdb_connection.h" +#include "ikvdb_sync_interface.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 std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait); + + // Enable auto sync + void EnableAutoSync(bool enable); + + // Stop a sync action in progress. + void StopSync(int syncId); + + // 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 = true); + + NotificationChain::Listener *AddRemotePushFinishedNotify(const RemotePushFinishedNotifier ¬ifier, int errCode); + + void NotifyRemotePushFinishedInner(const std::string &targetId) const; + +protected: + virtual IKvDBSyncInterface *GetSyncInterface() = 0; + + // Start syncer + void StartSyncer(); + + // Stop syncer + void StopSyncer(); + + // 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: + SyncerProxy syncer_; + std::atomic started_; + mutable std::mutex remotePushNotifyChainLock_; + NotificationChain *remotePushNotifyChain_; + static const EventType REMOTE_PUSH_FINISHED; +}; +} + +#endif // SYNC_ABLE_KVDB_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.cpp new file mode 100755 index 000000000..94ac9f826 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "sync_able_kvdb.h" + +namespace DistributedDB { +SyncAbleKvDBConnection::SyncAbleKvDBConnection(SyncAbleKvDB *kvDB) + : GenericKvDBConnection(kvDB), + remotePushFinishedListener_(nullptr) +{ + OnKill([this]() { + for (const auto &syncId : syncIdList_) { + SyncAbleKvDB *db = GetDB(); + if (syncId <= 0 || db == nullptr) { + continue; + } + + // Drop the lock before we call RemoveSyncOperation(). + UnlockObj(); + db->StopSync(syncId); + LockObj(); + } + syncIdList_.clear(); + }); +} + +SyncAbleKvDBConnection::~SyncAbleKvDBConnection() +{ + if (remotePushFinishedListener_ != nullptr) { + remotePushFinishedListener_->Drop(true); + } + remotePushFinishedListener_ = nullptr; +} + +int SyncAbleKvDBConnection::Pragma(int cmd, void *parameter) +{ + int errCode = PragmaParamCheck(cmd, parameter); + if (errCode != E_OK) { + return -E_INVALID_ARGS; + } + switch (cmd) { + case PRAGMA_SYNC_DEVICES: + errCode = PragmaSyncAction(static_cast(parameter)); + break; + case PRAGMA_AUTO_SYNC: + errCode = EnableAutoSync(*(static_cast(parameter))); + break; + case PRAGMA_PERFORMANCE_ANALYSIS_GET_REPORT: + *(static_cast(parameter)) = PerformanceAnalysis::GetInstance()->GetStatistics(); + break; + case PRAGMA_PERFORMANCE_ANALYSIS_OPEN: + PerformanceAnalysis::GetInstance()->OpenPerformanceAnalysis(); + break; + case PRAGMA_PERFORMANCE_ANALYSIS_CLOSE: + PerformanceAnalysis::GetInstance()->ClosePerformanceAnalysis(); + break; + case PRAGMA_PERFORMANCE_ANALYSIS_SET_REPORTFILENAME: + PerformanceAnalysis::GetInstance()->SetFileNumber(*(static_cast(parameter))); + break; + case PRAGMA_GET_QUEUED_SYNC_SIZE: + errCode = GetQueuedSyncSize(static_cast(parameter)); + break; + case PRAGMA_SET_QUEUED_SYNC_LIMIT: + errCode = SetQueuedSyncLimit(static_cast(parameter)); + break; + case PRAGMA_GET_QUEUED_SYNC_LIMIT: + errCode = GetQueuedSyncLimit(static_cast(parameter)); + break; + case PRAGMA_SET_WIPE_POLICY: + errCode = SetStaleDataWipePolicy(static_cast(parameter)); + break; + case PRAGMA_REMOTE_PUSH_FINISHED_NOTIFY: + errCode = SetRemotePushFinishedNotify(static_cast(parameter)); + break; + default: + // Call Pragma() of super class. + errCode = GenericKvDBConnection::Pragma(cmd, parameter); + break; + } + return errCode; +} + +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, user are using a closed connection. + LOGE("Pragma sync on a closed connection."); + return -E_STALE; + } + IncObjRef(this); + } + int errCode = kvDB->Sync(syncParameter->devices_, syncParameter->mode_, + std::bind(&SyncAbleKvDBConnection::OnSyncComplete, this, std::placeholders::_1, syncParameter->onComplete_, + syncParameter->wait_), [this]() { + DecObjRef(this); + }, syncParameter->wait_); + if (errCode <= 0) { + DecObjRef(this); + } else if (!syncParameter->wait_) { + AutoLock lockGuard(this); + if (!IsKilled()) { + syncIdList_.push_back(errCode); + } + } + 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 (!wait && !IsKilled()) { + if (!syncIdList_.empty()) { + syncIdList_.pop_front(); + } + } + 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; +} +} diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.h b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.h new file mode 100755 index 000000000..c6c77128f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/sync_able_kvdb_connection.h @@ -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. + */ + +#ifndef SYNC_ABLE_KVDB_CONNECTION_H +#define SYNC_ABLE_KVDB_CONNECTION_H + +#include "ref_object.h" +#include "generic_kvdb_connection.h" + +namespace DistributedDB { +class SyncAbleKvDB; +struct PragmaSync; +struct PragmaRemotePushNotify; + +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); + + // 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); + + // For sync in progress. + std::list syncIdList_; + + std::mutex remotePushFinishedListenerLock_; + NotificationChain::Listener *remotePushFinishedListener_; +}; +} + +#endif // SYNC_ABLE_KVDB_CONNECTION_H diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/database_upgrader.h b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/database_upgrader.h new file mode 100644 index 000000000..2877a3d04 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp new file mode 100755 index 000000000..689fb6e52 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.cpp @@ -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. + */ + +#include "single_ver_database_upgrader.h" +#include "db_errno.h" +#include "log_print.h" +#include "version.h" + +namespace DistributedDB { +int SingleVerDatabaseUpgrader::Upgrade() +{ + LOGI("[SingleUp][Upgrade] Enter, CurVersion=%d.", SINGLE_VER_STORE_VERSION_CURRENT); + int errCode = GetDatabaseVersion(dbVersion_); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] GetVersion fail, errCode=%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] DbVersion=%d.", dbVersion_); + + errCode = BeginUpgrade(); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] Begin fail, errCode=%d.", errCode); + return errCode; + } + errCode = ExecuteUpgrade(); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] Execute fail, errCode=%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 fail, errCode=%d.", errCode); + EndUpgrade(false); + return errCode; + } + } + + errCode = EndUpgrade(true); + if (errCode != E_OK) { + LOGE("[SingleUp][Upgrade] End fail, errCode=%d.", errCode); + return errCode; + } + + LOGI("[SingleUp][Upgrade] Exit Successfully."); + return E_OK; +} + +int SingleVerDatabaseUpgrader::ExecuteUpgrade() +{ + if (dbVersion_ <= SINGLE_VER_STORE_VERSION_CURRENT) { + return UpgradeFromDatabaseVersion(dbVersion_); + } + return E_OK; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_database_upgrader.h new file mode 100644 index 000000000..fcdbae46a --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.cpp new file mode 100755 index 000000000..00b0560a8 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.h new file mode 100755 index 000000000..6f6016553 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/storage/src/upgrader/single_ver_schema_database_upgrader.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 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/services/distributeddataservice/libs/distributeddb/syncer/include/commit_history_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/include/commit_history_sync.h new file mode 100755 index 000000000..881f85dab --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/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 "multi_ver_kvdb_sync_interface.h" +#include "icommunicator.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 errorCode); + + void GetErrorCode(int32_t &errorCode) 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/services/distributeddataservice/libs/distributeddb/syncer/include/device_manager.h b/services/distributeddataservice/libs/distributeddb/syncer/include/device_manager.h new file mode 100644 index 000000000..82ad16481 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/device_manager.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 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 &callback); + + // 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/services/distributeddataservice/libs/distributeddb/syncer/include/isync_engine.h b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_engine.h new file mode 100755 index 000000000..fd66bb154 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_engine.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 I_SYNC_ENGINE_H +#define I_SYNC_ENGINE_H + +#include +#include + +#include "ikvdb_sync_interface.h" +#include "sync_operation.h" +#include "meta_data.h" +#include "ref_object.h" + +namespace DistributedDB { +class ISyncEngine : public virtual RefObject { +public: + // Do some init things + virtual int Initialize(IKvDBSyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged) = 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 inited + virtual void RegConnectCallback() = 0; + + // Get local deviceId, is hashed + virtual int GetLocalIdentity(std::string &outTarget) const = 0; + + virtual std::string GetLabel() const = 0; + +protected: + virtual ~ISyncEngine() {}; +}; +} // namespace DistributedDB + +#endif // I_SYNC_ENGINE_H \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/isync_state_machine.h b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_state_machine.h new file mode 100755 index 000000000..26994f19c --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_state_machine.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 I_SYNC_STATE_MACHINE_H +#define I_SYNC_STATE_MACHINE_H + +#include + +#include "sync_target.h" +#include "sync_task_context.h" +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" + +namespace DistributedDB { +class ISyncStateMachine { +public: + const static int RETRY_TIME = 3; + + virtual ~ISyncStateMachine() {}; + + // Init the SyncStateMachine, this function must be called before any other call. + virtual int Initialize(ISyncTaskContext *context, IKvDBSyncInterface *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; +}; +} // namespace DistributedDB + +#endif // I_SYNC_STATE_MACHINE_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/isync_target.h b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_target.h new file mode 100755 index 000000000..fc05ebcc0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_target.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 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 Sync Status, it will increase the ref of operation + virtual void SetSyncOperation(SyncOperation *operation) = 0; + + // Get a SyncOperation + virtual void GetSyncOperation(SyncOperation *&operation) const = 0; + + // Is this target is a auto sync + virtual bool IsAutoSync() const = 0; +}; +} // namespace DistributedDB + +#endif // I_SYNC_TARGET_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/isync_task_context.h b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_task_context.h new file mode 100755 index 000000000..95b5337a3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/isync_task_context.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sync_target.h" +#include "sync_operation.h" +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "time_helper.h" +#include "runtime_context.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, IKvDBSyncInterface *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; + + // 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; + +protected: + virtual ~ISyncTaskContext() {}; +}; +} // namespace DistributedDB + +#endif // I_SYNC_TASK_CONTEXT_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/isyncer.h b/services/distributeddataservice/libs/distributeddb/syncer/include/isyncer.h new file mode 100755 index 000000000..6fdf05bd2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/isyncer.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 I_SYNCER_H +#define I_SYNCER_H + +#include +#include +#include + +#include "ikvdb_sync_interface.h" +#include "types_export.h" + +namespace DistributedDB { +class ISyncer { +public: + virtual ~ISyncer() {}; + + // Init the Syncer modules + virtual int Initialize(IKvDBSyncInterface *syncInterface) = 0; + + // Close + virtual int Close() = 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; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + virtual int RemoveSyncOperation(int syncId) = 0; + + // Get The current vitural 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; + + // 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; +}; +} // namespace DistributedDB + +#endif // I_SYNCER_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/meta_data.h b/services/distributeddataservice/libs/distributeddb/syncer/include/meta_data.h new file mode 100755 index 000000000..1cb6356fe --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/meta_data.h @@ -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. + */ + +#ifndef META_DATA_H +#define META_DATA_H + +#include +#include +#include + +#include "db_types.h" +#include "ikvdb_sync_interface.h" +#include "ref_object.h" + +namespace DistributedDB { +struct MetaDataValue { + TimeOffset timeOffset = 0; + uint64_t lastUpdateTime = 0; + uint64_t localWaterMark = 0; + uint64_t peerWaterMark = 0; +}; + +class Metadata { +public: + Metadata(); + ~Metadata(); + + int Initialize(IKvDBSyncInterface *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 = true); + + int SaveLocalTimeOffset(TimeOffset timeOffset); + + TimeOffset GetLocalTimeOffset() const; + + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash = true); + + void SetLastLocalTime(TimeStamp lastLocalTime); + + TimeStamp GetLastLocalTime() const; + +private: + + int SaveMetaDataValue(const DeviceID &deviceId, const MetaDataValue &inValue); + + void GetMetaDataValue(const DeviceID &deviceId, MetaDataValue &outValue, bool isNeedHash = true); + + 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); + + int SetMetadataToDb(const std::vector &key, const std::vector &inValue); + + void PutMetadataToMap(const DeviceID &deviceId, const MetaDataValue &value); + + void GetMetadataFromMap(const DeviceID &deviceId, MetaDataValue &outValue); + + int64_t StringToLong(const std::vector &value); + + int GetAllMetadataKey(std::vector> &keys); + + int LoadAllMetadata(); + + uint64_t GetRandTimeOffset() const; + + void GetHashDeviceId(const DeviceID &deviceId, DeviceID &hashDeviceId, bool isNeedHash = true); + + // store localTimeOffset in ram; if change, should add a lock first, change here and metadata, + // then release lock + std::atomic localTimeOffset_; + std::mutex localTimeOffsetLock_; + IKvDBSyncInterface *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_; + std::mutex metadataLock_; + std::map deviceIdToHashDeviceIdMap_; + + // store localTimeOffset in ram, used to make timestamp increase + mutable std::mutex lastLocalTimeLock_; + TimeStamp lastLocalTime_; +}; +} // namespace DistributedDB +#endif diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/multi_ver_data_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/include/multi_ver_data_sync.h new file mode 100755 index 000000000..b8da3d072 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/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 "multi_ver_kvdb_sync_interface.h" +#include "icommunicator.h" +#include "sync_task_context.h" +#include "multi_ver_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 &errorCode) 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/services/distributeddataservice/libs/distributeddb/syncer/include/sync_operation.h b/services/distributeddataservice/libs/distributeddb/syncer/include/sync_operation.h new file mode 100755 index 000000000..995526d1f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/sync_operation.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 SYNC_OPERATION_H +#define SYNC_OPERATION_H + +#include +#include +#include +#include +#include + +#include "ikvdb_sync_interface.h" +#include "ref_object.h" +#include "semaphore.h" +#include "notification_chain.h" +#include "runtime_context.h" + +namespace DistributedDB { +class SyncOperation : public RefObject { +public: + enum Status { + WAITING = 0, + SYNCING, + SEND_FINISHED, + RECV_FINISHED, + FINISHED_ALL, // status >= FINISHED_ALL is final status. + TIMEOUT, + PERMISSION_CHECK_FAILED, + COMM_ABNORMAL, + SECURITY_OPTION_CHECK_FAILURE, // remote device's SecurityOption not equal to local + EKEYREVOKED_FAILURE, // EKEYREVOKED error + BUSY_FAILURE, + SCHEMA_INCOMPATIBLE, + FAILED + }; + + enum SyncMode { + PUSH, + PULL, + PUSH_AND_PULL, + AUTO_PUSH, + AUTO_PULL, + RESPONSE_PULL, + INVALID + }; + + 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); + + // 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; + + // Check if All devices sync finished. + bool CheckIsAllFinished() const; + +protected: + virtual ~SyncOperation(); + +private: + DECLARE_OBJECT_TAG(SyncOperation); + + // called by destruction + void Finalize(); + + // 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 a auto sync + bool isAutoSync_; + + // Is this operation has finished + bool isFinished_; + + // Used for block sync + std::unique_ptr semaphore_; +}; +} // namespace DistributedDB + +#endif // SYNC_OPERATION_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/sync_types.h b/services/distributeddataservice/libs/distributeddb/syncer/include/sync_types.h new file mode 100755 index 000000000..94ada2dea --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/sync_types.h @@ -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. + */ + +#ifndef SYNC_TYPES_H +#define SYNC_TYPES_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, +}; + +const static uint32_t SEND_TIME_OUT = 3000; // 3s +const int NOT_SURPPORT_SEC_CLASSIFICATION = 0xff; +} + +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/syncer_factory.h b/services/distributeddataservice/libs/distributeddb/syncer/include/syncer_factory.h new file mode 100644 index 000000000..20e1c45e2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/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/services/distributeddataservice/libs/distributeddb/syncer/include/syncer_proxy.h b/services/distributeddataservice/libs/distributeddb/syncer/include/syncer_proxy.h new file mode 100755 index 000000000..9e2ba913d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/syncer_proxy.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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" +#include "ikvdb_sync_interface.h" + +namespace DistributedDB { +class SyncerProxy : public ISyncer { +public: + SyncerProxy(); + ~SyncerProxy() {}; + + // Init the Syncer modules + int Initialize(IKvDBSyncInterface *syncInterface) override; + + // Close the syncer + int Close() 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; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + int RemoveSyncOperation(int syncId) 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; + + // 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; + +private: + std::mutex syncerLock_; + std::shared_ptr syncer_; +}; +} // namespace DistributedDB + +#endif // SYNCER_PROXY_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/time_helper.h b/services/distributeddataservice/libs/distributeddb/syncer/include/time_helper.h new file mode 100755 index 000000000..357f7d73e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/time_helper.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 TIME_HELPER_H +#define TIME_HELPER_H + +#include + +#include "meta_data.h" +#include "runtime_context.h" +#include "ref_object.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 IKvDBSyncInterface *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); + +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 IKvDBSyncInterface *storage_; + std::shared_ptr metadata_; +}; +} // namespace DistributedDB + +#endif // TIME_HELPER_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/include/value_slice_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/include/value_slice_sync.h new file mode 100755 index 000000000..6348c13e1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/include/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 "multi_ver_kvdb_sync_interface.h" +#include "icommunicator.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/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.cpp new file mode 100755 index 000000000..9d1cc7e07 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.cpp @@ -0,0 +1,931 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "single_ver_kvdb_sync_interface.h" +#include "single_ver_sync_task_context.h" + +namespace DistributedDB { +AbilitySyncRequestPacket::AbilitySyncRequestPacket() + : protocolVersion_(ABILITY_SYNC_VERSION_V1), + sendCode_(E_OK), + softwareVersion_(SOFTWARE_VERSION_CURRENT), + secLabel_(0), + secFlag_(0), + schemaType_(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; +} + +void AbilitySyncRequestPacket::GetSchema(std::string &schema) const +{ + schema = 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_; +} + +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_ + // 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:%llu", len); + return 0; + } + return len; +} + +AbilitySyncAckPacket::AbilitySyncAckPacket() + : protocolVersion_(ABILITY_SYNC_VERSION_V1), + softwareVersion_(SOFTWARE_VERSION_CURRENT), + ackCode_(E_OK), + secLabel_(0), + secFlag_(0), + schemaType_(0), + permitSync_(0), + requirePeerConvert_(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; +} + +void AbilitySyncAckPacket::GetSchema(std::string &schema) const +{ + schema = 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_; +} + +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_ + if (len > INT32_MAX) { + LOGE("[AbilitySyncAckPacket][CalculateLen] err len:%llu", len); + return 0; + } + return len; +} + +AbilitySync::AbilitySync() + : communicator_(nullptr), + storageInterface_(nullptr), + syncFinished_(false) +{ +} + +AbilitySync::~AbilitySync() +{ + communicator_ = nullptr; + storageInterface_ = nullptr; +} + +int AbilitySync::Initialize(ICommunicator *inCommunicator, IKvDBSyncInterface *inStorage, const std::string &deviceId) +{ + if (inCommunicator == nullptr || inStorage == nullptr || deviceId.empty()) { + return -E_INVALID_ARGS; + } + communicator_ = inCommunicator; + storageInterface_ = inStorage; + deviceId_ = deviceId; + return E_OK; +} + +int AbilitySync::SyncStart(uint32_t sessionId, uint32_t sequenceId, uint16_t remoteCommunicatorVersion, + const CommErrHandler &handler) +{ + Message *message = new (std::nothrow) Message(ABILITY_SYNC_MESSAGE); + if (message == nullptr) { + return -E_OUT_OF_MEMORY; + } + AbilitySyncRequestPacket packet; + packet.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + SchemaObject schemaObj = (static_cast(storageInterface_))->GetSchemaInfo(); + // 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(schemaObj.ToSchemaString()); + packet.SetSchemaType(static_cast(schemaObj.GetSchemaType())); + } + SecurityOption option; + GetPacketSecOption(option); + packet.SetSecLabel(option.securityLabel); + packet.SetSecFlag(option.securityFlag); + + message->SetMessageType(TYPE_REQUEST); + int 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); + LOGI("[AbilitySync][SyncStart] software version = %u, Label = %d, Flag = %d", SOFTWARE_VERSION_CURRENT, + option.securityLabel, option.securityFlag); + errCode = communicator_->SendMessage(deviceId_, message, false, SEND_TIME_OUT, 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) +{ + if (message == nullptr || context == nullptr) { + return -E_INVALID_ARGS; + } + const AbilitySyncAckPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = CheckAckCode(message, context, packet->GetAckCode()); + if (errCode != E_OK) { + return errCode; + } + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + std::string schema; + packet->GetSchema(schema); + SingleVerKvDBSyncInterface *storage = static_cast(storageInterface_); + if (remoteSoftwareVersion > SOFTWARE_VERSION_RELEASE_2_0) { + HandleVersionV3AckSecOptionParam(packet, context); + SyncOpinion localSyncOpinion = HandleVersionV3AckSchemaParam(packet, schema, context); + bool permitSync = ((static_cast(context))->GetSyncStrategy()).permitSync; + if (!permitSync) { + (static_cast(context))->SetTaskErrCode(-E_SCHEMA_MISMATCH); + LOGE("[AbilitySync][AckRecv] scheme check failed"); + return -E_SCHEMA_MISMATCH; + } + (void)SendAck(message, storage->GetSchemaInfo(), AbilitySync::CHECK_SUCCESS, localSyncOpinion, true); + (static_cast(context))->SetIsSchemaSync(true); + } else { + bool isCompatible = storage->CheckCompatible(schema); + 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 E_OK; +} + +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; + } + SyncOpinion localSyncOpinion; + SingleVerKvDBSyncInterface *storage = static_cast(storageInterface_); + if (packet->GetSendCode() == -E_VERSION_NOT_SUPPORT) { + (void)SendAck(message, storage->GetSchemaInfo(), -E_VERSION_NOT_SUPPORT, localSyncOpinion, false); + LOGI("[AbilitySync][RequestRecv] version can not support, remote version is %u", packet->GetProtocolVersion()); + return -E_VERSION_NOT_SUPPORT; + } + + std::string schema; + packet->GetSchema(schema); + bool isCompatible = storage->CheckCompatible(schema); + if (!isCompatible) { + (static_cast(context))->SetTaskErrCode(-E_SCHEMA_MISMATCH); + } + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + int ackCode; + if (remoteSoftwareVersion > SOFTWARE_VERSION_RELEASE_2_0) { + localSyncOpinion = HandleVersionV3RequestParam(packet, context, schema); + if (SecLabelCheck(packet)) { + ackCode = E_OK; + } else { + ackCode = -E_SECURITY_OPTION_CHECK_ERROR; + } + } else { + LOGI("[AbilitySync][RequestRecv] remote version = %u, CheckSchemaCompatible = %d", + remoteSoftwareVersion, isCompatible); + return SendAck(message, SchemaObject(), E_OK, localSyncOpinion, false); + } + LOGI("[AbilitySync][RequestRecv] remote dev = %s{private}, version = %u, CheckSchemaCompatible = %d", + deviceId_.c_str(), remoteSoftwareVersion, isCompatible); + return SendAck(message, storage->GetSchemaInfo(), ackCode, localSyncOpinion, false); +} + +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 a errCode %d", errCode); + return errCode; + } + std::string schema; + packet->GetSchema(schema); + uint32_t remoteSoftwareVersion = packet->GetSoftwareVersion(); + context->SetRemoteSoftwareVersion(remoteSoftwareVersion); + SyncOpinion localSyncOpinion = HandleVersionV3AckSchemaParam(packet, schema, context); + LOGI("[AckNotifyRecv] receive dev = %s{private} ack notify, remoteSoftwareVersion = %u, ackCode = %d", + deviceId_.c_str(), remoteSoftwareVersion, errCode); + (static_cast(context))->SetIsSchemaSync(true); + (void)SendAck(message, SchemaObject(), AbilitySync::LAST_NOTIFY, localSyncOpinion, true); + return E_OK; +} + +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; + } +} + +SyncOpinion AbilitySync::HandleVersionV3RequestParam(const AbilitySyncRequestPacket *packet, ISyncTaskContext *context, + const std::string &remoteSchema) const +{ + int32_t remoteSecLabel = packet->GetSecLabel(); + int32_t remoteSecFlag = packet->GetSecFlag(); + SecurityOption secOption = {remoteSecLabel, remoteSecFlag}; + (static_cast(context))->SetRemoteSeccurityOption(secOption); + (static_cast(context))->SetReceivcPermitCheck(false); + uint8_t remoteSchemaType = packet->GetSchemaType(); + SchemaObject localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + SyncOpinion localSyncOpinion = SchemaObject::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); + LOGI("[AbilitySync][HandleVersionV3RequestParam] remoteSecLabel = %d, remoteSecFlag = %d, remoteSchemaType = %u", + remoteSecLabel, remoteSecFlag, remoteSchemaType); + return localSyncOpinion; +} + +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); +} + +SyncOpinion AbilitySync::HandleVersionV3AckSchemaParam(const AbilitySyncAckPacket *packet, + const std::string &remoteSchema, ISyncTaskContext *context) const +{ + bool permitSync = static_cast(packet->GetPermitSync()); + bool requirePeerConvert = static_cast(packet->GetRequirePeerConvert()); + SyncOpinion remoteOpinion = {permitSync, requirePeerConvert, true}; + uint8_t remoteSchemaType = packet->GetSchemaType(); + SchemaObject localSchema = (static_cast(storageInterface_))->GetSchemaInfo(); + SyncOpinion localOpinion = SchemaObject::MakeLocalSyncOpinion(localSchema, remoteSchema, remoteSchemaType); + SyncStrategy localStrategy = SchemaObject::ConcludeSyncStrategy(localOpinion, remoteOpinion); + (static_cast(context))->SetSyncStrategy(localStrategy); + return localOpinion; +} + +void AbilitySync::GetPacketSecOption(SecurityOption &option) +{ + int 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::SendAck(const Message *inMsg, const SchemaObject &schemaObj, int ackCode, SyncOpinion localOpinion, + 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; + } + + AbilitySyncAckPacket ackPacket; + ackPacket.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + ackPacket.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + ackPacket.SetSchema(schemaObj.ToSchemaString()); + ackPacket.SetSchemaType(static_cast(schemaObj.GetSchemaType())); + ackPacket.SetAckCode(ackCode); + if (!isAckNotify) { + SecurityOption option; + GetPacketSecOption(option); + ackPacket.SetSecLabel(option.securityLabel); + ackPacket.SetSecFlag(option.securityFlag); + } + ackPacket.SetPermitSync(localOpinion.permitSync); + ackPacket.SetRequirePeerConvert(localOpinion.requirePeerConvert); + 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()); + + errCode = communicator_->SendMessage(deviceId_, ackMessage, false, SEND_TIME_OUT); + if (errCode != E_OK) { + LOGE("[AbilitySync][SendAck] SendPacket failed, err %d", errCode); + delete ackMessage; + ackMessage = nullptr; + } + return errCode; +} + +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); + int errCode = parcel.WriteUInt32(packet->GetProtocolVersion()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetSendCode()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(packet->GetSoftwareVersion()); + if (errCode != E_OK) { + return errCode; + } + std::string schema; + packet->GetSchema(schema); + errCode = parcel.WriteString(schema); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetSecLabel()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetSecFlag()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(packet->GetSchemaType()); + if (errCode != E_OK) { + return errCode; + } + 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); + int errCode = parcel.WriteUInt32(ABILITY_SYNC_VERSION_V1); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(SOFTWARE_VERSION_CURRENT); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetAckCode()); + if (errCode != E_OK) { + return errCode; + } + std::string schema; + packet->GetSchema(schema); + errCode = parcel.WriteString(schema); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetSecLabel()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetSecFlag()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(packet->GetSchemaType()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(packet->GetPermitSync()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteUInt32(packet->GetRequirePeerConvert()); + if (errCode != E_OK) { + return errCode; + } + 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); + if (!parcel.IsError() && softwareVersion > SOFTWARE_VERSION_RELEASE_2_0) { + RequestPacketDeSerializationTailPart(parcel, packet); + } + if (parcel.IsError()) { + 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; +} + +void AbilitySync::RequestPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncRequestPacket *packet) +{ + 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); +} + +void AbilitySync::AckPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncAckPacket *packet) +{ + 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); +} + +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); + if (!parcel.IsError() && softwareVersion > SOFTWARE_VERSION_RELEASE_2_0) { + AckPacketDeSerializationTailPart(parcel, packet); + } + if (parcel.IsError()) { + 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::CheckAckCode(const Message *message, ISyncTaskContext *context, int errCode) +{ + if (message->GetErrorNo() == E_FEEDBACK_UNKNOWN_MESSAGE) { + LOGE("[AbilitySync][AckRecv] Remote device dose not support this message id"); + context->SetRemoteSoftwareVersion(SOFTWARE_VERSION_EARLIEST); + return -E_FEEDBACK_UNKNOWN_MESSAGE; + } + + if (errCode != E_OK) { + LOGE("[AbilitySync][AckRecv] received a errCode %d", errCode); + if (errCode == -E_SECURITY_OPTION_CHECK_ERROR) { + context->SetTaskErrCode(-E_SECURITY_OPTION_CHECK_ERROR); + } + return errCode; + } + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.h new file mode 100755 index 000000000..4304e1856 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/ability_sync.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 ABILITY_SYNC_H +#define ABILITY_SYNC_H + +#include +#include + +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "isync_task_context.h" +#include "parcel.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); + void GetSchema(std::string &schema) 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; + + uint32_t CalculateLen() const; + +private: + uint32_t protocolVersion_; + int32_t sendCode_; + uint32_t softwareVersion_; + std::string schema_; + int32_t secLabel_; + int32_t secFlag_; + uint32_t schemaType_; +}; + +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); + void GetSchema(std::string &schema) 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; + + uint32_t CalculateLen() 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_; +}; + +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, IKvDBSyncInterface *inStorage, 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: + int SendAck(const Message *inMsg, const SchemaObject &schemaObj, int ackCode, SyncOpinion localOpinion, + bool isAckNotify = false); + + 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 void RequestPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncRequestPacket *packet); + + static void AckPacketDeSerializationTailPart(Parcel &parcel, AbilitySyncAckPacket *packet); + + bool SecLabelCheck(const AbilitySyncRequestPacket *packet) const; + + SyncOpinion HandleVersionV3RequestParam(const AbilitySyncRequestPacket *packet, ISyncTaskContext *context, + const std::string &remoteSchema) const; + + void HandleVersionV3AckSecOptionParam(const AbilitySyncAckPacket *packet, + ISyncTaskContext *context) const; + + SyncOpinion HandleVersionV3AckSchemaParam(const AbilitySyncAckPacket *packet, const std::string &remoteSchema, + ISyncTaskContext *context) const; + + void GetPacketSecOption(SecurityOption &option); + + int CheckAckCode(const Message *message, ISyncTaskContext *context, int errCode); + + ICommunicator *communicator_; + IKvDBSyncInterface *storageInterface_; + std::string deviceId_; + bool syncFinished_; + static const int FAILED_GET_SEC_CLASSIFICATION = 0x55; +}; +} // namespace DistributedDB +#endif // ABILITY_SYNC_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/commit_history_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/commit_history_sync.cpp new file mode 100755 index 000000000..8eff48509 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/commit_history_sync.cpp @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +{ + 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 = %d, 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(commits.size()); + LOGD("CommitHistorySync::AckRecvCallback end, CommitsSize = %llu, 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 = %llu", 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 = %lu, cac len = %llu", + 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) +{ + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, false, SEND_TIME_OUT); + 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/services/distributeddataservice/libs/distributeddb/syncer/src/device_manager.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/device_manager.cpp new file mode 100755 index 000000000..ea9be0c48 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/device_manager.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) { return DeviceManager::CalculateLen(); }; + // LocalDataChanged has no dataPct + func.serializeFunc = [](uint8_t *buffer, uint32_t length, const Message *inMsg) { return E_OK; }; + func.deserializeFunc = [](const uint8_t *buffer, uint32_t length, Message *inMsg) { return E_OK; }; + return MessageTransform::RegTransformFunction(LOCAL_DATA_CHANGED, func); +} + +// Initialize the DeviceManager +int DeviceManager::Initialize(ICommunicator *communicator, const std::function &callback) +{ + if (communicator == nullptr) { + return -E_INVALID_ARGS; + } + RefObject::IncObjRef(communicator); + communicator_ = communicator; + RegDeviceOnLineCallBack(callback); + + 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:%d", 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); + int errCode = communicator_->SendMessage(deviceId, msg, false, SEND_TIME_OUT); + 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/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.cpp new file mode 100755 index 000000000..1c933f24d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_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" + +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) + +{ +} + +GenericSyncer::~GenericSyncer() +{ + LOGD("[GenericSyncer] ~GenericSyncer!"); + if (syncEngine_ != nullptr) { + RefObject::KillAndDecObjRef(syncEngine_); + syncEngine_ = nullptr; + } + timeHelper_ = nullptr; + metadata_ = nullptr; + syncInterface_ = nullptr; +} + +int GenericSyncer::Initialize(IKvDBSyncInterface *syncInterface) +{ + 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; + } + + // 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; + } + + if (!RuntimeContext::GetInstance()->IsCommunicatorAggregatorValid()) { + LOGW("[Syncer] Communicator component not ready!"); + return -E_NOT_INIT; + } + + int errCode = SyncModuleInit(); + if (errCode != E_OK) { + LOGE("[Syncer] Sync ModuleInit ERR!"); + return -E_INTERNAL_ERROR; + } + + errCode = InitSyncEngine(syncInterface); + if (errCode != E_OK) { + return errCode; + } + + initialized_ = true; + } + + // RegConnectCallback may start a auto sync, this function can not in syncerLock_ + syncEngine_->RegConnectCallback(); + return E_OK; +} + +int GenericSyncer::Close() +{ + { + std::lock_guard lock(syncerLock_); + if (!initialized_) { + LOGW("[Syncer] Syncer don't need to close, because it has no been init."); + return -E_NOT_INIT; + } + initialized_ = false; + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + closing_ = true; + } + ClearSyncOperations(); + if (syncEngine_ != nullptr) { + syncEngine_->OnKill([this]() { this->syncEngine_->Close(); }); + LOGD("[Syncer] Close SyncEngine!"); + RefObject::KillAndDecObjRef(syncEngine_); + std::lock_guard lock(syncerLock_); + syncEngine_ = nullptr; + 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) +{ + std::lock_guard lock(syncerLock_); + if (!initialized_) { + LOGE("[Syncer] Syncer is not initialized, return!"); + return -E_NOT_INIT; + } + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + if (!IsValidDevices(devices) || !IsValidMode(mode)) { + return -E_INVALID_ARGS; + } + if (IsQueuedManualSyncFull(mode, wait)) { + LOGE("[Syncer] -E_BUSY"); + return -E_BUSY; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SYNC_TOTAL); + } + uint32_t syncId = GenerateSyncId(); + LOGI("[Syncer] GenerateSyncId %d, mode = %d, wait = %d , label = %s, devicesNum = %d", syncId, mode, wait, + label_.c_str(), devices.size()); + SyncOperation *operation = new (std::nothrow) SyncOperation(syncId, devices, mode, onComplete, wait); + if (operation == nullptr) { + LOGE("[Syncer] SyncOperation alloc failed when sync called, may be out of memory"); + return -E_OUT_OF_MEMORY; + } + 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(onFinalize); + int errCode = AddSyncOperation(operation); + if (errCode != E_OK) { + LOGE("[Syncer] AddSyncOperation failed when sync called, err %d", errCode); + RefObject::KillAndDecObjRef(operation); + return errCode; + } + AddQueuedManualSyncSize(mode, wait); + LOGD("[Syncer] AddSyncOperation end"); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SYNC_TOTAL); + } + return syncId; +} + +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())) { + SubQueuedSyncSize(); + } + operation->NotifyIfNeed(); + RefObject::KillAndDecObjRef(operation); + operation = nullptr; + return E_OK; + } + return -E_INVALID_ARGS; +} + +uint64_t GenericSyncer::GetTimeStamp() +{ + if (timeHelper_ == nullptr) { + return TimeHelper::GetSysCurrentTime(); + } + return timeHelper_->GetTime(); +} + +int GenericSyncer::AddSyncOperation(SyncOperation *operation) +{ + LOGD("[Syncer] AddSyncOperation."); + if (operation == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = syncEngine_->AddSyncOperation(operation); + if (errCode != E_OK) { + return errCode; + } + + if (operation->CheckIsAllFinished()) { + if (operation->IsBlockSync()) { + operation->Finished(); + RefObject::KillAndDecObjRef(operation); + return errCode; + } + RefObject::IncObjRef(operation); + RefObject::IncObjRef(syncEngine_); + ISyncEngine *syncEngine = syncEngine_; + errCode = RuntimeContext::GetInstance()->ScheduleTask([operation, syncEngine, this] { + std::lock_guard lock(syncerLock_); + if (closing_) { + LOGI("[Syncer] Syncer is closing, return!"); + RefObject::DecObjRef(operation); + RefObject::DecObjRef(syncEngine); + return; + } + operation->Finished(); + RefObject::KillAndDecObjRef(operation); + RefObject::DecObjRef(operation); + RefObject::DecObjRef(syncEngine); + }); + if (errCode != E_OK) { + LOGE("[Syncer] AddSyncOperation start finish task errCode:%d", errCode); + RefObject::DecObjRef(operation); + RefObject::DecObjRef(syncEngine_); + } + return errCode; + } + { + std::lock_guard lock(operationMapLock_); + syncOperationMap_.insert(std::pair(operation->GetSyncId(), operation)); + // To make sure operation alive before WaitIfNeed out + RefObject::IncObjRef(operation); + } + operation->WaitIfNeed(); + RefObject::DecObjRef(operation); + return errCode; +} + +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(IKvDBSyncInterface *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(IKvDBSyncInterface *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(IKvDBSyncInterface *syncInterface) +{ + syncEngine_ = CreateSyncEngine(); + if (syncEngine_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + + syncEngine_->OnLastRef([]() { LOGD("[Syncer] SyncEngine finalized"); }); + const std::function func = std::bind(&GenericSyncer::RemoteDataChanged, + this, std::placeholders::_1); + int errCode = syncEngine_->Initialize(syncInterface, metadata_, func); + 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; + } +} + +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 > SyncOperation::AUTO_PULL) || (mode < SyncOperation::PUSH)) { + LOGE("[Syncer] Sync mode is not valid!"); + return false; + } + return true; +} + +bool GenericSyncer::IsValidDevices(const std::vector &devices) const +{ + if (devices.empty()) { + LOGE("[Syncer] devices is empty!"); + return false; + } + return true; +} + +void GenericSyncer::ClearSyncOperations() +{ + std::lock_guard lock(operationMapLock_); + for (auto &iter : syncOperationMap_) { + RefObject::KillAndDecObjRef(iter.second); + iter.second = nullptr; + } + syncOperationMap_.clear(); +} + +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 = SingleVerDataSync::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; +} + +void GenericSyncer::AddQueuedManualSyncSize(int mode, bool wait) +{ + if (((mode == SyncOperation::PULL) || (mode == SyncOperation::PUSH) || (mode == SyncOperation::PUSH_AND_PULL)) && + (wait == false)) { + std::lock_guard lock(queuedManualSyncLock_); + queuedManualSyncSize_++; + } +} + +bool GenericSyncer::IsQueuedManualSyncFull(int mode, bool wait) const +{ + std::lock_guard lock(queuedManualSyncLock_); + if (((mode == SyncOperation::PULL) || (mode == SyncOperation::PUSH) || (mode == SyncOperation::PUSH_AND_PULL)) && + (manualSyncEnable_ == false)) { + LOGI("[GenericSyncer] manualSyncEnable_:false"); + return true; + } + if (((mode == SyncOperation::PULL) || (mode == SyncOperation::PUSH) || (mode == SyncOperation::PUSH_AND_PULL)) && + (wait == false)) { + if (queuedManualSyncSize_ < queuedManualSyncLimit_) { + return false; + } + LOGD("[GenericSyncer] queuedManualSyncSize_:%d < queuedManualSyncLimit_:%d", queuedManualSyncSize_, + queuedManualSyncLimit_); + return true; + } + 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::lock_guard lock(syncerLock_); + if (!initialized_) { + LOGE("[Syncer] Syncer is not initialized, return!"); + return -E_NOT_INIT; + } + if (closing_) { + LOGE("[Syncer] Syncer is closing, return!"); + return -E_BUSY; + } + if (syncEngine_ == nullptr) { + LOGE("[Syncer] Syncer syncEngine_ is nullptr, return!"); + return -E_NOT_INIT; + } + return syncEngine_->GetLocalIdentity(outTarget); +} + +void GenericSyncer::GetOnlineDevices(std::vector &devices) const +{ + // Get devices from AutoLaunch first. + if (syncInterface_ == nullptr) { + LOGI("[Syncer] GetOnlineDevices syncInterface_ is nullptr"); + return; + } + std::string identifier = syncInterface_->GetDbProperties().GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""); + // If this database is configured as autoLaunch, then this database on other devices is probably autoLaunch as well. + // Since this database on other devices might have not been opened, we have to use physical online devices as list. + // If this database is not configured as autoLaunch, the out devices will be empty. + 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); + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.h b/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.h new file mode 100755 index 000000000..c02195a5f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/generic_syncer.h @@ -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. + */ + +#ifndef GENRIC_SYNCER_H +#define GENRIC_SYNCER_H + +#include +#include +#include + +#include "isyncer.h" +#include "isync_engine.h" +#include "meta_data.h" +#include "time_helper.h" +#include "sync_operation.h" + +namespace DistributedDB { +class GenericSyncer : public virtual ISyncer { +using DataChangedFunc = std::function; + +public: + GenericSyncer(); + virtual ~GenericSyncer(); + + // Init the Syncer modules + int Initialize(IKvDBSyncInterface *syncInterface) override; + + // Close + int Close() 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; + + // Remove the operation, with the given syncId, used to clean resource if sync finished or failed. + int RemoveSyncOperation(int syncId) 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; + +protected: + // Remote data changed callback + virtual void RemoteDataChanged(const std::string &device) = 0; + + // Create a sync engine, if has memory error, will return nullptr. + virtual ISyncEngine *CreateSyncEngine() = 0; + + // Add a Sync Operation, after call this function, the operation will be start + virtual int 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(IKvDBSyncInterface *syncInterface); + + // Init the TimeHelper + int InitTimeHelper(IKvDBSyncInterface *syncInterface); + + // Init the Sync engine + int InitSyncEngine(IKvDBSyncInterface *syncInterface); + + // 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; + + // Check if the devices arg is valid + bool IsValidDevices(const std::vector &devices) const; + + // Used Clear all SyncOperations. + void ClearSyncOperations(); + + // Callback when the special sync finished. + void OnSyncFinished(int syncId); + + void AddQueuedManualSyncSize(int mode, bool wait); + + bool IsQueuedManualSyncFull(int mode, bool wait) const; + + void SubQueuedSyncSize(void); + + void GetOnlineDevices(std::vector &devices) const; + + 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_; + + ISyncEngine *syncEngine_; + IKvDBSyncInterface *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_; +}; +} // namespace DistributedDB + +#endif // GENRIC_SYNCER_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/meta_data.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/meta_data.cpp new file mode 100755 index 000000000..753d41274 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/meta_data.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "securec.h" +#include "db_errno.h" +#include "db_common.h" +#include "log_print.h" +#include "time_helper.h" +#include "hash.h" + +namespace DistributedDB { +namespace { + const int STR_TO_LL_BY_DEX = 10; + // store local timeoffset;this is a special key; + const std::string LOCALTIMEOFFSET_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(IKvDBSyncInterface* storage) +{ + naturalStoragePtr_ = storage; + std::vector key; + std::vector timeOffset; + DBCommon::StringToVector(LOCALTIMEOFFSET_KEY, key); + + int errCode = GetMetadataFromDb(key, timeOffset); + if (errCode == -E_NOT_FOUND) { + uint64_t randTimeOffset = GetRandTimeOffset(); + SaveLocalTimeOffset(TimeHelper::BASE_OFFSET + randTimeOffset); + } else { + localTimeOffset_ = StringToLong(timeOffset); + } + { + std::lock_guard lockGuard(metadataLock_); + metadataMap_.clear(); + } + return LoadAllMetadata(); +} + +int Metadata::SaveTimeOffset(const DeviceID &deviceId, TimeOffset inValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata); + metadata.timeOffset = inValue; + metadata.lastUpdateTime = TimeHelper::GetSysCurrentTime(); + LOGD("Metadata::SaveTimeOffset = %lld dev %s{private}", inValue, deviceId.c_str()); + return SaveMetaDataValue(deviceId, metadata); +} + +void Metadata::GetTimeOffset(const DeviceID &deviceId, TimeOffset &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata); + outValue = metadata.timeOffset; +} + +void Metadata::GetLocalWaterMark(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata); + outValue = metadata.localWaterMark; +} + +int Metadata::SaveLocalWaterMark(const DeviceID &deviceId, uint64_t inValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata); + metadata.localWaterMark = inValue; + LOGD("Metadata::SaveLocalWaterMark = %llu\n", inValue); + return SaveMetaDataValue(deviceId, metadata); +} + +void Metadata::GetPeerWaterMark(const DeviceID &deviceId, uint64_t &outValue) +{ + MetaDataValue metadata; + std::lock_guard lockGuard(metadataLock_); + GetMetaDataValue(deviceId, metadata); + 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 = %llu", 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( + LOCALTIMEOFFSET_KEY.begin(), LOCALTIMEOFFSET_KEY.end()); + + std::lock_guard lockGuard(localTimeOffsetLock_); + localTimeOffset_ = timeOffset; + LOGD("Metadata::SaveLocalTimeOffset offset = %lld\n", timeOffset); + int errCode = SetMetadataToDb(localTimeOffsetValue, timeOffsetValue); + if (errCode != E_OK) { + LOGE("Metadata::SaveLocalTimeOffset SetMetadataToDb failed errCode:%d", errCode); + } + return errCode; +} + +TimeOffset Metadata::GetLocalTimeOffset() const +{ + return localTimeOffset_.load(std::memory_order_seq_cst); +} + +int Metadata::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + return SavePeerWaterMark(deviceId, 0, isNeedHash); +} + +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); + std::vector key; + DBCommon::StringToVector(hashDeviceId, key); + errCode = SetMetadataToDb(key, value); + if (errCode != E_OK) { + LOGE("Metadata::SetMetadataToDb failed errCode:%d\n", 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) +{ + 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); +} + +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) +{ + std::string valueString(value.begin(), value.end()); + int64_t longData = std::strtoll(valueString.c_str(), nullptr, STR_TO_LL_BY_DEX); + LOGD("Metadata::StringToLong longData = %lld\n", longData); + return longData; +} + +int Metadata::GetAllMetadataKey(std::vector> &keys) +{ + if (naturalStoragePtr_ == nullptr) { + return -E_INVALID_DB; + } + return naturalStoragePtr_->GetAllMetaKeys(keys); +} + +int Metadata::LoadAllMetadata() +{ + std::vector> deviceIds; + std::vector value; + int errCode = E_OK; + GetAllMetadataKey(deviceIds); + + for (auto it = deviceIds.begin(); it != deviceIds.end(); ++it) { + if (it->size() < DEVICEID_PREFIX_KEY.size()) { + continue; + } + std::string prefixKey(it->begin(), it->begin() + DEVICEID_PREFIX_KEY.size()); + if (prefixKey != DEVICEID_PREFIX_KEY) { + continue; + } + errCode = GetMetadataFromDb(*it, value); + if (errCode != E_OK) { + return errCode; + } + MetaDataValue metadata; + std::string deviceId(it->begin(), it->end()); + errCode = DeSerializeMetaData(value, metadata); + { + std::lock_guard lockGuard(metadataLock_); + PutMetadataToMap(deviceId, metadata); + } + } + 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 %llu", 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]; + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_data_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_data_sync.cpp new file mode 100755 index 000000000..bf7d17bff --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_data_sync.cpp @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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(entries.size()); + LOGD("MultiVerDataSync::AckRecvCallback src=%s{private}, entries num = %llu", + 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 = %llu", + 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) +{ + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, false, SEND_TIME_OUT); + 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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.cpp new file mode 100755 index 000000000..54bbe224e --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_engine.h new file mode 100755 index 000000000..0c23ae95c --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp new file mode 100755 index 000000000..ba3231deb --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { +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, IKvDBSyncInterface *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{private}] step to idle", context_->GetDeviceId().c_str()); +} + +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 && 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() +{ + { + std::lock_guard lock(stateMachineLock_); + 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{private}, state = %d", + context_->GetDeviceId().c_str(), 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) +{ +} + +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{private}", context_->GetDeviceId().c_str()); + 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{private}, MergeSyncCommit errCode:%d", + context_->GetDeviceId().c_str(), errCode); + if (performance != nullptr) { + performance->StepTimeRecordEnd(MV_TEST_RECORDS::RECORD_MERGE); + } + } + RefObject::AutoLock lock(context_); + context_->SetOperationStatus(SyncOperation::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{private}, commitIndex = %d,", + context_->GetDeviceId().c_str(), commitIndex); + if (commitIndex > 0) { + context_->GetCommit(commitIndex - 1, commit); + deviceName = context_->GetDeviceId(); + context_->GetEntries(entries); + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish src=%s{private}, entries size = %lu", + context_->GetDeviceId().c_str(), entries.size()); + errCode = timeSync_->GetTimeOffset(outOffset); + if (errCode != E_OK) { + LOGI("MultiVerSyncStateMachine::OneCommitSyncFinish GetTimeOffset fail errCode:%d", errCode); + return errCode; + } + TimeStamp currentLocalTime = context_->GetCurrentLocalTime(); + commit.timestamp -= 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 - currentLocalTime); + LOGD("MultiVerSyncStateMachine::OneCommitSyncFinish src=%s{private}, timefixOffset = %lld", + context_->GetDeviceId().c_str(), timefixOffset); + commit.timestamp -= timefixOffset; + for (MultiVerKvEntry *entry : entries) { + TimeStamp timeStamp; + entry->GetTimestamp(timeStamp); + timeStamp = timeStamp - outOffset - timefixOffset; + entry->SetTimestamp(timeStamp); + } + 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{private}, errCode = %d", + context_->GetDeviceId().c_str(), 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 errCode = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(context_); }); + if (errCode != E_OK) { + LOGE("[MultiVerSyncStateMachine][SyncResponseEnd] timer finalizer ScheduleTask, errCode %d", + errCode); + } + }, + 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 %d", timerId); + return E_OK; + } + sessionId = iter->sessionId; + } + SyncResponseEnd(sessionId); + return E_OK; +} +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.h new file mode 100755 index 000000000..312e34730 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_state_machine.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sync_state_machine.h" +#include "multi_ver_sync_task_context.h" +#include "meta_data.h" +#include "time_sync.h" +#include "commit_history_sync.h" +#include "multi_ver_data_sync.h" +#include "value_slice_sync.h" +#include "db_types.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, IKvDBSyncInterface *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() 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) 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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_target.h b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_target.h new file mode 100644 index 000000000..aea804816 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.cpp new file mode 100755 index 000000000..f4bd65a6a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_task_context.h" +#include "multi_ver_sync_state_machine.h" +#include "multi_ver_sync_target.h" +#include "log_print.h" + +namespace DistributedDB { +DEFINE_OBJECT_TAG_FACILITIES(MultiVerSyncTaskContext) + +MultiVerSyncTaskContext::~MultiVerSyncTaskContext() +{ +} + +int MultiVerSyncTaskContext::Initialize(const std::string &deviceId, IKvDBSyncInterface *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); + } + 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::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) +{ + SyncTaskContext::CopyTargetData(target); +} +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.h b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_sync_task_context.h new file mode 100755 index 000000000..6f03d1656 --- /dev/null +++ b/services/distributeddataservice/libs/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 "sync_task_context.h" +#include "multi_ver_kvdb_sync_interface.h" + +namespace DistributedDB { +class MultiVerSyncTaskContext final : public SyncTaskContext { +public: + MultiVerSyncTaskContext() {}; + + DISABLE_COPY_ASSIGN_MOVE(MultiVerSyncTaskContext); + + // Init the MultiVerSyncTaskContext + int Initialize(const std::string &deviceId, IKvDBSyncInterface *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) 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/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.cpp new file mode 100755 index 000000000..94b1a8d26 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.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. + */ + +#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 syncId = Sync(devices, SyncOperation::AUTO_PULL, nullptr, nullptr, false); + if (syncId < MIN_VALID_SYNC_ID) { + LOGE("[Syncer] sync start by EnableAutoSync failed err %d", syncId); + } +} + +int MultiVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + 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; + devices.push_back(device); + int syncId = Sync(devices, SyncOperation::AUTO_PULL, nullptr, nullptr, false); + if (syncId < MIN_VALID_SYNC_ID) { + LOGE("[MultiVerSyncer] sync start by RemoteDataChanged failed err %d", syncId); + } + } +} + +ISyncEngine *MultiVerSyncer::CreateSyncEngine() +{ + return new (std::nothrow) MultiVerSyncEngine(); +} + +int MultiVerSyncer::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + return -E_INVALID_ARGS; + } + MultiVerKvDBSyncInterface *syncInterface = static_cast(syncInterface_); + syncInterface->NotifyStartSyncOperation(); + return 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) +{ + return -E_NOT_SUPPORT; +} +} // namespace DistributedDB +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.h b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_syncer.h new file mode 100755 index 000000000..65f985bc4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/multi_ver_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 MULTI_VER_SYNCER_H +#define MULTI_VER_SYNCER_H + +#ifndef OMIT_MULTI_VER +#include "macro_utils.h" +#include "generic_syncer.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; + + // Local data changed callback + void LocalDataChanged(int notifyEvent) override; + + // Remote data changed callback + void RemoteDataChanged(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 + int 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/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.cpp new file mode 100755 index 000000000..9bce54fc6 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.cpp @@ -0,0 +1,1865 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "single_ver_sync_state_machine.h" +#include "performance_analysis.h" +#include "message_transform.h" +#include "generic_single_ver_kv_entry.h" +#include "single_ver_sync_target.h" +#include "db_constant.h" + +namespace DistributedDB { +namespace { + // 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 ...} + constexpr uint32_t REQUEST_PACKET_RESERVED_INDEX_PACKETID = 0; + constexpr uint32_t ACK_PACKET_RESERVED_INDEX_PACKETID = 1; + constexpr uint32_t ACK_PACKET_RESERVED_INDEX_LOCAL_WATER_MARK = 0; // index 0 for localWaterMark + + void 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); + } +} + +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::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); +} + +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() const +{ + uint64_t totalLen = GenericSingleVerKvEntry::CalculateLens(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; + } + 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; +} + +void DataRequestPacket::SetLastSequence() +{ + flag_ = flag_ | IS_LAST_SEQUENCE; +} + +void DataRequestPacket::SetBasicInfo(int sendCode, uint32_t version, WaterMark localMark, WaterMark peerMark, + int32_t mode) +{ + SetSendCode(sendCode); + SetVersion(version); + SetLocalWaterMark(localMark); + SetPeerWaterMark(peerMark); + SetMode(mode); +} + +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) { + return DataAckReserve[ACK_PACKET_RESERVED_INDEX_PACKETID]; + } else { + 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; +} + +SingleVerDataSync::SingleVerDataSync() + : mtuSize_(0), + storage_(nullptr), + communicateHandle_(nullptr), + metadata_(nullptr) +{ +} + +SingleVerDataSync::~SingleVerDataSync() +{ + storage_ = nullptr; + communicateHandle_ = nullptr; + metadata_ = nullptr; +} + +int SingleVerDataSync::RegisterTransformFunc() +{ + TransformFunc func; + func.computeFunc = std::bind(&SingleVerDataSync::CalculateLen, std::placeholders::_1); + func.serializeFunc = std::bind(&SingleVerDataSync::Serialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + func.deserializeFunc = std::bind(&SingleVerDataSync::DeSerialization, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + return MessageTransform::RegTransformFunction(DATA_SYNC_MESSAGE, func); +} + +int SingleVerDataSync::Initialize(IKvDBSyncInterface *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_ = communicateHandle_->GetCommunicatorMtuSize(deviceId) * 9 / 10; // get the 9/10 of the size. + std::vector label = inStorage->GetIdentifier(); + label.resize(3); // only show 3 Bytes enough + label_ = DBCommon::VectorToHexString(label); + deviceId_ = deviceId; + return E_OK; +} + +bool SingleVerDataSync::IsPacketValid(const Message *inMsg) +{ + if (inMsg == nullptr) { + return false; + } + if (inMsg->GetMessageId() != DATA_SYNC_MESSAGE) { + LOGE("[DataSync][IsPacketValid] Message Id ERROR! messageId=%d", inMsg->GetMessageId()); + 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 SingleVerDataSync::Serialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg))) { + return -E_MESSAGE_ID_ERROR; + } + + 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 SingleVerDataSync::DeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg) +{ + if ((buffer == nullptr) || !(IsPacketValid(inMsg))) { + return -E_MESSAGE_ID_ERROR; + } + + 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; + } +} + +uint32_t SingleVerDataSync::CalculateLen(const Message *inMsg) +{ + if (!(IsPacketValid(inMsg))) { + return 0; + } + uint32_t len = 0; + int errCode; + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + errCode = DataPacketCalculateLen(inMsg, len); + if (errCode != E_OK) { + LOGE("[DataSync][CalculateLen] 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("[DataSync][CalculateLen] calculate data notify packet len failed errCode=%d", errCode); + return 0; + } + return len; + default: + return 0; + } +} + +int SingleVerDataSync::DataPacketCalculateLen(const Message *inMsg, uint32_t &len) +{ + const DataRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + + len = packet->CalculateLen(); + return E_OK; +} + +int SingleVerDataSync::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; +} + +std::string SingleVerDataSync::GetLocalDeviceName() +{ + std::string deviceInfo; + if (communicateHandle_ != nullptr) { + communicateHandle_->GetLocalIdentity(deviceInfo); + } + return deviceInfo; +} + +std::string SingleVerDataSync::TransferForeignOrigDevName(const std::string &deviceName) +{ + // Get the hash device name. + std::string localName = GetLocalDeviceName(); + if (DBCommon::TransferHashString(localName) == deviceName) { + return ""; + } + return deviceName; +} + +std::string SingleVerDataSync::TransferLocalOrigDevName(const std::string &origName) +{ + std::string localName = GetLocalDeviceName(); + if (origName.empty()) { + return DBCommon::TransferHashString(localName); + } + return origName; +} + +int SingleVerDataSync::DataPacketSyncerPartSerialization(Parcel &parcel, const DataRequestPacket *packet) +{ + // endWaterMark + int errCode = parcel.WriteUInt64(packet->GetEndWaterMark()); + if (errCode != E_OK) { + return errCode; + } + // localWaterMark + errCode = parcel.WriteUInt64(packet->GetLocalWaterMark()); + if (errCode != E_OK) { + return errCode; + } + // peerWaterMark + errCode = parcel.WriteUInt64(packet->GetPeerWaterMark()); + if (errCode != E_OK) { + return errCode; + } + // sendCode + errCode = parcel.WriteInt(packet->GetSendCode()); + if (errCode != E_OK) { + return errCode; + } + // mode + errCode = parcel.WriteInt(packet->GetMode()); + if (errCode != E_OK) { + return errCode; + } + // sessionId + errCode = parcel.WriteUInt32(packet->GetSessionId()); + if (errCode != E_OK) { + return errCode; + } + // reserved + errCode = parcel.WriteVector(packet->GetReserved()); + if (errCode != E_OK) { + return errCode; + } + if (packet->GetVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + errCode = parcel.WriteUInt32(packet->GetFlag()); + if (errCode != E_OK) { + return errCode; + } + } + parcel.EightByteAlign(); + return errCode; +} + +int SingleVerDataSync::DataPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg) +{ + const DataRequestPacket *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) { + return errCode; + } + // sendDataItems + const std::vector &data = packet->GetData(); + errCode = GenericSingleVerKvEntry::SerializeDatas(data, parcel, packet->GetVersion()); + if (errCode != E_OK) { + return errCode; + } + return SingleVerDataSync::DataPacketSyncerPartSerialization(parcel, packet); +} + +int SingleVerDataSync::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 (packLen != length || parcel.IsError()) { + LOGE("[DataSync][DataPacketDeSerialization] deserialize failed! input len=%lu,packLen=%lu", length, packLen); + return -E_LENGTH_ERROR; + } + 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 SingleVerDataSync::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_INVALID_ARGS; + } + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket(); + if (packet == nullptr) { + return -E_OUT_OF_MEMORY; + } + if (version > SOFTWARE_VERSION_CURRENT) { + packet->SetVersion(version); + packet->SetSendCode(-E_VERSION_NOT_SUPPORT); + int errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; + } + packet->SetVersion(version); + packLen += GenericSingleVerKvEntry::DeSerializeDatas(dataItems, parcel); + packet->SetData(dataItems); + int errCode = SingleVerDataSync::DataPacketSyncerPartDeSerialization(parcel, packet, packLen, length, version); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + return errCode; + } + errCode = inMsg->SetExternalObject<>(packet); + if (errCode != E_OK) { + delete packet; + packet = nullptr; + } + return errCode; +} + +int SingleVerDataSync::AckPacketSyncerPartSerializationV1(Parcel &parcel, const DataAckPacket *packet) +{ + int errCode = parcel.WriteUInt64(packet->GetData()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteInt(packet->GetRecvCode()); + if (errCode != E_OK) { + return errCode; + } + errCode = parcel.WriteVector(packet->GetReserved()); + if (errCode != E_OK) { + return errCode; + } + parcel.EightByteAlign(); + return errCode; +} + +int SingleVerDataSync::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); + int errCode = parcel.WriteUInt32(packet->GetVersion()); + if (errCode != E_OK) { + return errCode; + } + // now V1 compatible for softWareVersion :{101, 102} + return SingleVerDataSync::AckPacketSyncerPartSerializationV1(parcel, packet); +} + +int SingleVerDataSync::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()) { + return -E_INVALID_ARGS; + } + packet.SetData(mark); + packet.SetRecvCode(errCode); + packet.SetReserved(reserved); + return E_OK; +} + +int SingleVerDataSync::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 = SingleVerDataSync::AckPacketSyncerPartDeSerializationV1(parcel, packet); + if (errCode != E_OK) { + return errCode; + } + + return inMsg->SetCopiedObject<>(packet); +} + +int SingleVerDataSync::Send(SingleVerSyncTaskContext *context, const Message *message, const CommErrHandler &handler, + uint32_t packetLen) +{ + bool startFeedDogRet = false; + if (packetLen > mtuSize_ && mtuSize_ > 0) { + uint32_t time = static_cast(static_cast(packetLen) * + static_cast(DBConstant::AUTO_SYNC_TIMEOUT) / mtuSize_); // no overflow + startFeedDogRet = context->StartFeedDogForSync(time, SyncDirectionFlag::SEND); + } + int errCode = communicateHandle_->SendMessage(context->GetDeviceId(), message, false, SEND_TIME_OUT, 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; + 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_OK || errCode == -E_UNFINISHED) { + TransDbDataItemToSendDataItem(context, outData); + } + if (errCode == -E_UNFINISHED) { + LOGD("[DataSync][GetData] not finished."); + } + if (errCode == -E_EKEYREVOKED) { + context->SetTaskErrCode(-E_EKEYREVOKED); + } + if (errCode == -E_BUSY) { + context->SetTaskErrCode(-E_BUSY); + } + return errCode; +} + +int SingleVerDataSync::GetDataWithRerformanceRecord(SingleVerSyncTaskContext *context, + std::vector &outData) +{ + 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; + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_READ_DATA); + } + int errCode = GetData(context, outData, packetSize); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_READ_DATA); + } + return errCode; +} + +int SingleVerDataSync::GetUnsyncData(SingleVerSyncTaskContext *context, std::vector &outData, + size_t packetSize) +{ + WaterMark startMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), startMark); + WaterMark endMark = context->GetEndMark(); + if ((endMark == 0) || (startMark > endMark)) { + return E_OK; + } + ContinueToken token = nullptr; + context->GetContinueToken(token); + if (token != nullptr) { + storage_->ReleaseContinueToken(token); + } + DataSizeSpecInfo syncDataSizeInfo = {mtuSize_, packetSize}; + int errCode = storage_->GetSyncData(startMark, endMark, outData, token, syncDataSizeInfo); + context->SetContinueToken(token); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + 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 = {mtuSize_, packetSize}; + int errCode = storage_->GetSyncDataNext(outData, token, syncDataSizeInfo); + context->SetContinueToken(token); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + LOGE("[DataSync][GetNextUnsyncData] get next unsync data failed, errCode=%d", errCode); + } + return errCode; +} + +int SingleVerDataSync::SaveData(const SingleVerSyncTaskContext *context, const std::vector &inData) +{ + if (inData.empty()) { + return E_OK; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SAVE_DATA); + } + + TransSendDataItemToLocal(context, inData); + int errCode = storage_->PutSyncData(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; +} + +TimeStamp SingleVerDataSync::GetMaxSendDataTime(const std::vector &inData, bool isNeedInit, + WaterMark localMark) +{ + TimeStamp stamp = 0; + if (isNeedInit) { + stamp = localMark; + } + 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; +} + +TimeStamp SingleVerDataSync::GetMinSendDataTime(const std::vector &inData, WaterMark localMark) +{ + TimeStamp stamp = localMark; + 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; +} + +int SingleVerDataSync::SaveLocalWaterMark(const DeviceID &deviceId, WaterMark waterMark) +{ + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SAVE_LOCAL_WATERMARK); + } + int errCode = metadata_->SaveLocalWaterMark(deviceId, waterMark); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SAVE_LOCAL_WATERMARK); + } + if (errCode != E_OK) { + LOGE("[DataSync][SaveLocalWaterMark] save metadata local watermark failed,errCode=%d", errCode); + } + return errCode; +} + +int SingleVerDataSync::SavePeerWaterMark(const DeviceID &deviceId, WaterMark waterMark) +{ + return metadata_->SavePeerWaterMark(deviceId, waterMark); +} + +int SingleVerDataSync::RemoveDeviceData(SingleVerSyncTaskContext *context, const Message *message, + WaterMark maxSendDataTime) +{ + int errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + (void)SendAck(context, message, errCode, maxSendDataTime); + return errCode; + } + if (context->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_EARLIEST) { + (void)SaveLocalWaterMark(context->GetDeviceId(), 0); // avoid repeat clear in ack + } + return E_OK; +} + +void SingleVerDataSync::TransDbDataItemToSendDataItem(const SingleVerSyncTaskContext *context, + std::vector &outData) +{ + for (size_t i = 0; i < outData.size(); i++) { + if (outData[i] == nullptr) { + continue; + } + outData[i]->SetOrigDevice(TransferLocalOrigDevName(outData[i]->GetOrigDevice())); + if (i == 0 || i == (outData.size() - 1)) { + LOGD("[DataSync][TransToSendItem] printData packet=%d,timeStamp=%llu,flag=%llu", i, + outData[i]->GetTimestamp(), outData[i]->GetFlag()); + } + } +} + +void SingleVerDataSync::TransSendDataItemToLocal(const SingleVerSyncTaskContext *context, + 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())); + TimeStamp tempTimestamp = item->GetTimestamp(); + TimeStamp tempWriteTimestamp = item->GetWriteTimestamp(); + item->SetTimestamp(tempTimestamp - offset); + if (tempWriteTimestamp != 0) { + item->SetWriteTimestamp(tempWriteTimestamp - offset); + } + + if (item->GetTimestamp() > currentLocalTime) { + item->SetTimestamp(currentLocalTime); + } + if (item->GetWriteTimestamp() > currentLocalTime) { + item->SetWriteTimestamp(currentLocalTime); + } + } +} + +int SingleVerDataSync::PushStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + std::vector outData; + WaterMark localMark; + WaterMark peerMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + LOGD("[DataSync][PushStart] localMark=%llu,endmark=%llu,peerMark=%llu,label=%s,dev=%s{private}", localMark, + context->GetEndMark(), peerMark, label_.c_str(), GetDeviceId().c_str()); + // get data + int errCode = GetDataWithRerformanceRecord(context, outData); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + 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; + } + bool isUpdateWaterMark = (outData.size() > 0); + TimeStamp maxSendDateTime = GetMaxSendDataTime(outData, true, localMark); + TimeStamp minSendDateTime = GetMinSendDataTime(outData, localMark); + context->SetSequenceStartAndEndTimeStamp(minSendDateTime, maxSendDateTime); + if (errCode == E_OK) { + context->SetSessionEndTimeStamp(maxSendDateTime); + packet->SetLastSequence(); + } + packet->SetData(outData); + packet->SetBasicInfo(errCode, version, localMark, peerMark, SyncOperation::PUSH); + SetPacketId(packet, context, version); + errCode = SendDataPacket(packet, context); + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_MACHINE_START_TO_PUSH_SEND); + } + if (errCode == E_OK && isUpdateWaterMark) { + SaveLocalWaterMark(context->GetDeviceId(), maxSendDateTime + 1); + } + return errCode; +} + +int SingleVerDataSync::PushPullStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + WaterMark localMark; + WaterMark peerMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + LOGD("[DataSync][PushPull] localMark=%llu,endmark=%llu,peerMark=%llu,label=%s,dev=%s{private}", localMark, + context->GetEndMark(), peerMark, label_.c_str(), GetDeviceId().c_str()); + // get data + std::vector outData; + int errCode = GetDataWithRerformanceRecord(context, outData); + // once get data occur E_EKEYREVOKED error, should also send request to remote dev to pull data. + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0 && errCode == -E_EKEYREVOKED) { + errCode = E_OK; + } + if (errCode != E_OK && errCode != -E_UNFINISHED) { + return errCode; + } + + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][PushPullStart] new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + bool isUpdateWaterMark = (outData.size() > 0); + TimeStamp maxSendDateTime = GetMaxSendDataTime(outData, true, localMark); + TimeStamp minSendDateTime = GetMinSendDataTime(outData, localMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + context->SetSequenceStartAndEndTimeStamp(minSendDateTime, maxSendDateTime); + if (errCode == E_OK) { + context->SetSessionEndTimeStamp(maxSendDateTime); + packet->SetLastSequence(); + } + packet->SetData(outData); + packet->SetBasicInfo(errCode, version, localMark, peerMark, SyncOperation::PUSH_AND_PULL); + packet->SetEndWaterMark(context->GetEndMark()); + packet->SetSessionId(context->GetRequestSessionId()); + SetPacketId(packet, context, version); + int sendErrCode = SendDataPacket(packet, context); + if (sendErrCode == E_OK && isUpdateWaterMark) { + SaveLocalWaterMark(context->GetDeviceId(), maxSendDateTime + 1); + } + return sendErrCode; +} + +int SingleVerDataSync::PullRequestStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][PullRequest]new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + WaterMark peerMark; + WaterMark localMark; + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + packet->SetLocalWaterMark(localMark); + packet->SetPeerWaterMark(peerMark); + packet->SetMode(SyncOperation::PULL); + WaterMark endMark = context->GetEndMark(); + packet->SetEndWaterMark(endMark); + packet->SetSessionId(context->GetRequestSessionId()); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + packet->SetVersion(version); + packet->SetLastSequence(); + SetPacketId(packet, context, version); + LOGD("[DataSync][PullRequest]peerMark=%llu,localMark=%llu,endMark=%llu,IsLastSeq=%d,label=%s,dev=%s{private}", + peerMark, localMark, endMark, packet->IsLastSequence(), label_.c_str(), GetDeviceId().c_str()); + return SendDataPacket(packet, context); +} + +int SingleVerDataSync::PullResponseStart(SingleVerSyncTaskContext *context) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + WaterMark localMark; + WaterMark peerMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + LOGD("[DataSync][PullResponse] localMark=%llu,pullendmark=%llu,peerMark=%llu,label=%s,dev=%s{private}", + localMark, context->GetEndMark(), peerMark, label_.c_str(), GetDeviceId().c_str()); + // get data + std::vector outData; + int errCode = GetDataWithRerformanceRecord(context, outData); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + SendPullResponseDataPkt(errCode, outData, 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; + } + bool isUpdateWaterMark = (outData.size() > 0); + TimeStamp maxSendDateTime = GetMaxSendDataTime(outData, true, localMark); + TimeStamp minSendDateTime = GetMinSendDataTime(outData, localMark); + context->SetSequenceStartAndEndTimeStamp(minSendDateTime, maxSendDateTime); + if (errCode == E_OK) { + context->SetSessionEndTimeStamp(maxSendDateTime); + } + errCode = SendPullResponseDataPkt(ackCode, outData, context); + if (errCode == E_OK && isUpdateWaterMark) { + SaveLocalWaterMark(context->GetDeviceId(), maxSendDateTime + 1); + } + return errCode; +} + +void SingleVerDataSync::UpdatePeerWaterMark(const SingleVerSyncTaskContext *context, WaterMark peerWatermark) +{ + if (peerWatermark == 0) { + return; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_SAVE_PEER_WATERMARK); + } + + int errCode = SavePeerWaterMark(context->GetDeviceId(), peerWatermark + 1); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_SAVE_PEER_WATERMARK); + } + if (errCode != E_OK) { + LOGE("[DataSync][UpdatePeerWaterMark] save peer water mark failed,errCode=%d", errCode); + } +} + +int SingleVerDataSync::RequestRecvPre(SingleVerSyncTaskContext *context, const Message *message) +{ + if (context == nullptr || message == nullptr) { + return -E_INVALID_ARGS; + } + const DataRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + if (context->GetRemoteSoftwareVersion() <= SOFTWARE_VERSION_BASE) { + uint16_t remoteCommunicatorVersion = 0; + if (communicateHandle_->GetRemoteCommunicatorVersion(context->GetDeviceId(), remoteCommunicatorVersion) == + -E_NOT_FOUND) { + LOGE("[DataSync][RequestRecvPre] 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][RequestRecvPre] need do ability sync"); + SendAck(context, message, -E_NEED_ABILITY_SYNC, 0); + return -E_WAIT_NEXT_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)SendAck(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][RequestRecvPre] remote pullResponse getData sendCode=%d", sendCode); + return sendCode; + } + int errCode = RunPermissionCheck(context, message, packet); + if (errCode != E_OK) { + return errCode; + } + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + errCode = CheckSchemaStrategy(context, message); + } + return errCode; +} + +int SingleVerDataSync::RequestRecv(SingleVerSyncTaskContext *context, const Message *message, + WaterMark &pullEndWatermark) +{ + int errCode = RequestRecvPre(context, message); + if (errCode != E_OK) { + return errCode; + } + const DataRequestPacket *packet = message->GetObject(); + const std::vector &data = packet->GetData(); + LOGI("[DataSync][RequestRecv] remote ver=%u,size=%d,errCode=%d,Label=%s,dev=%s{private}", packet->GetVersion(), + data.size(), packet->GetSendCode(), label_.c_str(), GetDeviceId().c_str()); + WaterMark packetLocalMark = packet->GetLocalWaterMark(); + WaterMark peerMark; + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + context->SetReceiveWaterMarkErr(false); + WaterMark maxSendDataTime = GetMaxSendDataTime(data, false); + if (packetLocalMark > peerMark) { + LOGI("[DataSync][RequestRecv] packetLocalMark=%llu,current=%llu", packetLocalMark, peerMark); + return SendLocalWaterMarkAck(context, message); + } else if ((packetLocalMark == 0) && (peerMark != 0) && context->IsNeedClearRemoteStaleData()) { + // need to clear remote device history data + errCode = RemoveDeviceData(context, message, maxSendDataTime); + if (errCode != E_OK) { + return errCode; + } + } + GetPullEndWatermark(context, packet, pullEndWatermark); + // save data first + errCode = SaveData(context, data); + if (errCode != E_OK) { + (void)SendAck(context, message, errCode, maxSendDataTime); + return errCode; + } + if (pullEndWatermark > 0 && !storage_->IsReadable()) { // pull mode + pullEndWatermark = 0; + errCode = SendAck(context, message, -E_EKEYREVOKED, maxSendDataTime); + } else { + // if data is empty, we don't know the max timestap of this packet. + errCode = SendAck(context, message, !data.empty() ? E_OK : WATER_MARK_INVALID, maxSendDataTime); + } + RemotePushFinished(packet->GetSendCode(), packet->GetMode(), message->GetSessionId(), + context->GetRequestSessionId()); + UpdatePeerWaterMark(context, maxSendDataTime); + if (errCode != E_OK) { + return errCode; + } + if (packet->GetSendCode() == SEND_FINISHED) { + return -E_RECV_FINISHED; + } + + return errCode; +} + +int SingleVerDataSync::SendDataPacket(const DataRequestPacket *packet, SingleVerSyncTaskContext *context) +{ + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (message == nullptr) { + LOGE("[DataSync][SendDataPacket] 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][SendDataPacket] set external object failed errCode=%d", errCode); + return errCode; + } + 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, std::vector &inData, + SingleVerSyncTaskContext *context) +{ + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][SendPullResponseDataPkt] new data request packet error"); + return -E_OUT_OF_MEMORY; + } + size_t size = inData.size(); + WaterMark localMark; + WaterMark peerMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + packet->SetBasicInfo(ackCode, version, localMark, peerMark, SyncOperation::PUSH); + if (ackCode == SEND_FINISHED) { + packet->SetLastSequence(); + } + SetPacketId(packet, context, version); + packet->SetData(inData); + uint32_t packetLen = packet->CalculateLen(); + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (message == nullptr) { + LOGE("[DataSync][SendPullResponseDataPkt] new message error"); + delete packet; + packet = nullptr; + return -E_OUT_OF_MEMORY; + } + LOGI("[DataSync][SendPullResponseDataPkt] size=%d,code=%d,LastSequence=%d,label=%s,dev=%s{private}", + size, ackCode, packet->IsLastSequence(), label_.c_str(), GetDeviceId().c_str()); + 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; + } + 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::SetAckData(DataAckPacket &ackPacket, SingleVerSyncTaskContext *context, int32_t recvCode, + WaterMark maxSendDataTime) const +{ + // 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; + metadata_->GetPeerWaterMark(context->GetDeviceId(), mark); + ackPacket.SetData(mark); + } +} + +int SingleVerDataSync::SendAck(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(DATA_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("[DataSync][SendAck] new message error"); + return -E_OUT_OF_MEMORY; + } + DataAckPacket ack; + ack.SetRecvCode(recvCode); + SetAckData(ack, context, recvCode, maxSendDataTime); + WaterMark localMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + 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); + } + ack.SetReserved(reserved); + ack.SetVersion(version); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][SendAck] set copied object failed, errcode=%d", errCode); + return errCode; + } + SetMessageHeadInfo(*ackMessage, TYPE_RESPONSE, context->GetDeviceId(), message->GetSequenceId(), + message->GetSessionId()); + + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + if (performance != nullptr) { + performance->StepTimeRecordEnd(PT_TEST_RECORDS::RECORD_DATA_REQUEST_RECV_TO_SEND_ACK); + } + return errCode; +} + +int SingleVerDataSync::SendLocalWaterMarkAck(SingleVerSyncTaskContext *context, const Message *message) +{ + context->SetReceiveWaterMarkErr(true); + const DataRequestPacket *packet = message->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + Message *ackMessage = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("[DataSync][LocalWaterMarkAck] new message error"); + return -E_OUT_OF_MEMORY; + } + + DataAckPacket ack; + WaterMark peerMark; + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + ack.SetData(peerMark); + WaterMark localMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + uint64_t packetId = 0; + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + packetId = packet->GetPacketId(); // above 102 version data request reserve[0] store packetId value + } + std::vector reserved {localMark}; + if (version > SOFTWARE_VERSION_RELEASE_2_0 && packetId > 0) { + reserved.push_back(packetId); + } + ack.SetReserved(reserved); + ack.SetRecvCode(LOCAL_WATER_MARK_NOT_INIT); + ack.SetVersion(version); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][LocalWaterMarkAck] set copied object failed, errcode=%d", errCode); + return errCode; + } + SetMessageHeadInfo(*ackMessage, TYPE_RESPONSE, context->GetDeviceId(), message->GetSequenceId(), + message->GetSessionId()); + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } + LOGI("[DataSync][LocalWaterMarkAck] LOCAL_WATER_MARK_NOT_INIT peerMark=%llu,localMark=%llu", peerMark, localMark); + return errCode; +} + +int SingleVerDataSync::AckRecv(SingleVerSyncTaskContext *context, const Message *message) +{ + if (context == nullptr || message == nullptr) { + return -E_INVALID_ARGS; + } + + 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{private}", packet->GetVersion(), + recvCode, SOFTWARE_VERSION_CURRENT, label_.c_str(), GetDeviceId().c_str()); + 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) { + // after set sliding window sender err, we can ReleaseContinueToken, avoid crash + context->SetSlidingWindowSenderErr(true); + LOGI("[DataSync][AckRecv] Data sync abort,recvCode = %d,label = %s,dev = %s{private}", recvCode, + label_.c_str(), GetDeviceId().c_str()); + 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=%llu,label=%s,dev=%s{private}", data, label_.c_str(), + GetDeviceId().c_str()); + } + + if (recvCode != E_OK && recvCode != WATER_MARK_INVALID) { + LOGW("[DataSync][AckRecv] Received a uncatched recvCode=%d,label=%s,dev=%s{private}", recvCode, + label_.c_str(), GetDeviceId().c_str()); + 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) +{ + Message *ackMessage = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + 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; + } + 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{private}", + errCode, label_.c_str(), GetDeviceId().c_str()); +} + +void SingleVerDataSync::GetPullEndWatermark(const SingleVerSyncTaskContext *context, const DataRequestPacket *packet, + WaterMark &pullEndWatermark) const +{ + if (packet == nullptr) { + return; + } + if ((packet->GetMode() == SyncOperation::PULL) || (packet->GetMode() == SyncOperation::PUSH_AND_PULL)) { + WaterMark endMark = packet->GetEndWaterMark(); + TimeOffset offset; + metadata_->GetTimeOffset(context->GetDeviceId(), offset); + pullEndWatermark = endMark - offset; + LOGD("[DataSync][PullEndWatermark] packetEndMark=%llu,offset=%llu,endWaterMark=%llu,label=%s,dev=%s{private}", + endMark, offset, pullEndWatermark, label_.c_str(), GetDeviceId().c_str()); + } +} + +int SingleVerDataSync::DealWaterMarkException(SingleVerSyncTaskContext *context, WaterMark ackWaterMark, + const std::vector &reserved) +{ + // after set sliding window sender err, we can SaveLocalWaterMark, avoid sliding window sender re save a wrong + // waterMark again. + context->SetSlidingWindowSenderErr(true); + WaterMark localMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + LOGI("[DataSync][WaterMarkException] AckRecv LOCAL_WATER_MARK_NOT_INIT mark=%llu,label=%s,dev=%s{private}", + ackWaterMark, label_.c_str(), GetDeviceId().c_str()); + int errCode = SaveLocalWaterMark(context->GetDeviceId(), ackWaterMark); + if (errCode != E_OK) { + return errCode; + } + context->SetRetryStatus(SyncTaskContext::NEED_RETRY); + // for push_and_pull mode it may be EKEYREVOKED error before receive watermarkexception + // should clear errCode and restart pushpull request. + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0 && + context->GetMode() == SyncOperation::PUSH_AND_PULL && context->GetTaskErrCode() == -E_EKEYREVOKED) { + context->SetTaskErrCode(E_OK); + } + + if (reserved.empty()) { + if (localMark != 0 && ackWaterMark == 0 && context->IsNeedClearRemoteStaleData()) { + // need to clear remote historydata + LOGI("[DataSync][WaterMarkException] AckRecv rebuilted,clear historydata,label=%s,dev=%s{private}", + label_.c_str(), GetDeviceId().c_str()); + errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + return errCode; + } + } + } else { + WaterMark peerMark; + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + if (reserved[ACK_PACKET_RESERVED_INDEX_LOCAL_WATER_MARK] == 0 && peerMark != 0 && + context->IsNeedClearRemoteStaleData()) { + // need to clear remote historydata + LOGI("[DataSync][WaterMarkException] AckRecv reserved not empty,rebuilted,clear historydata,label=%s," + "dev = %s{private}", label_.c_str(), GetDeviceId().c_str()); + errCode = storage_->RemoveDeviceData(context->GetDeviceId(), true); + if (errCode != E_OK) { + return errCode; + } + } + } + return -E_RE_SEND_DATA; +} + +int SingleVerDataSync::RunPermissionCheck(SingleVerSyncTaskContext *context, const Message *message, + const DataRequestPacket *packet) +{ + 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; + int32_t mode = packet->GetMode(); + if (mode == SyncOperation::PUSH) { + flag = CHECK_FLAG_RECEIVE; + } else if (mode == SyncOperation::PULL) { + flag = CHECK_FLAG_SEND; + } else if (mode == SyncOperation::PUSH_AND_PULL) { + flag = CHECK_FLAG_SEND | CHECK_FLAG_RECEIVE; + } else { + // before add permissionCheck, PushStart packet and pullResponse packet do not setMode. + flag = CHECK_FLAG_RECEIVE; + } + int errCode = RuntimeContext::GetInstance()->RunPermissionCheck(userId, appId, storeId, context->GetDeviceId(), + flag); + if (errCode != E_OK) { + LOGE("[DataSync][RunPermissionCheck] check failed flag=%d,Label=%s,dev=%s{private}", flag, label_.c_str(), + GetDeviceId().c_str()); + if (context->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_EARLIEST) { // ver 101 can't handle this errCode + (void)SendAck(context, message, -E_NOT_PERMIT, 0); + } + return -E_NOT_PERMIT; + } + const std::vector &data = packet->GetData(); + WaterMark maxSendDataTime = GetMaxSendDataTime(data, false); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + if (version > SOFTWARE_VERSION_RELEASE_2_0 && (mode != SyncOperation::PULL) && !context->GetReceivcPermitCheck()) { + bool permitReceive = CheckPermitReceiveData(context); + if (permitReceive) { + context->SetReceivcPermitCheck(true); + } else { + (void)SendAck(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) +{ + if (mtuSize_ >= packetLen || mtuSize_ == 0) { + return; + } + uint64_t data = static_cast(packetLen) * static_cast(DBConstant::AUTO_SYNC_TIMEOUT) / 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; + } + 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{private}", errCode, + label_.c_str(), GetDeviceId().c_str()); + } else { + LOGI("[DataSync][ResetWatchDog] data = %llu,label=%s,dev=%s{private}", data, label_.c_str(), + GetDeviceId().c_str()); + } +} + +void SingleVerDataSync::SendAck(SingleVerSyncTaskContext *context, uint32_t sessionId, uint32_t sequenceId, + uint64_t packetId) +{ + Message *ackMessage = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (ackMessage == nullptr) { + LOGE("[DataSync][SendAck] new message error"); + return; + } + DataAckPacket ack; + ack.SetRecvCode(E_OK); + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + WaterMark localMark; + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + std::vector reserved {localMark}; + if (version > SOFTWARE_VERSION_RELEASE_2_0 && packetId > 0) { + reserved.push_back(packetId); + } + ack.SetReserved(reserved); + ack.SetVersion(version); + int errCode = ackMessage->SetCopiedObject(ack); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + LOGE("[DataSync][SendAck] set copied object failed, errcode=%d", errCode); + return; + } + SetMessageHeadInfo(*ackMessage, TYPE_RESPONSE, context->GetDeviceId(), sequenceId, sessionId); + errCode = Send(context, ackMessage, nullptr, 0); + if (errCode != E_OK) { + delete ackMessage; + ackMessage = nullptr; + } +} + +int32_t SingleVerDataSync::ReSend(SingleVerSyncTaskContext *context, DataSyncReSendInfo reSendInfo) +{ + if (context == nullptr) { + return -E_INVALID_ARGS; + } + LOGI("[DataSync][ReSend] start=%llu,end=%llu,label=%s,dev=%s{private}", reSendInfo.start, reSendInfo.end, + label_.c_str(), GetDeviceId().c_str()); + std::vector outData; + 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 = {mtuSize_, packetSize}; + int errCode = storage_->GetSyncData(reSendInfo.start, reSendInfo.end + 1, outData, token, reSendDataSizeInfo); + if (token != nullptr) { + storage_->ReleaseContinueToken(token); + } + if (errCode == -E_BUSY || errCode == -E_EKEYREVOKED) { + context->SetTaskErrCode(errCode); + return errCode; + } + if (errCode != E_OK && errCode != -E_UNFINISHED) { + return errCode; + } + WaterMark localMark; + WaterMark peerMark; + metadata_->GetPeerWaterMark(context->GetDeviceId(), peerMark); + metadata_->GetLocalWaterMark(context->GetDeviceId(), localMark); + DataRequestPacket *packet = new (std::nothrow) DataRequestPacket; + if (packet == nullptr) { + LOGE("[DataSync][ReSend] new DataRequestPacket error"); + return -E_OUT_OF_MEMORY; + } + if (context->GetSessionEndTimeStamp() == reSendInfo.end) { + LOGI("[DataSync][ReSend] lastid,label=%s,dev=%s{private}", label_.c_str(), GetDeviceId().c_str()); + packet->SetLastSequence(); + } + packet->SetData(outData); + packet->SetBasicInfo(errCode, version, reSendInfo.start, peerMark, SyncOperation::PUSH); + if (version > SOFTWARE_VERSION_RELEASE_2_0) { + std::vector reserved {reSendInfo.packetId}; + packet->SetReserved(reserved); + } + errCode = SendReSendPacket(packet, context, reSendInfo.sessionId, reSendInfo.sequenceId); + if (errCode == E_OK && localMark < reSendInfo.end) { + // resend.end may not update in localwatermark while E_TIMEOUT occurred in send message last time. + SaveLocalWaterMark(context->GetDeviceId(), reSendInfo.end + 1); + } + return errCode; +} + +int SingleVerDataSync::SendReSendPacket(const DataRequestPacket *packet, SingleVerSyncTaskContext *context, + uint32_t sessionId, uint32_t sequenceId) +{ + Message *message = new (std::nothrow) Message(DATA_SYNC_MESSAGE); + if (message == nullptr) { + LOGE("[DataSync][SendDataPacket] 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][SendReSendPacket] SetExternalObject failed errCode=%d", errCode); + return errCode; + } + 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; +} + +bool SingleVerDataSync::IsPermitRemoteDeviceRecvData(const std::string &deviceId, + const SecurityOption &remoteSecOption) const +{ + 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); +} + +bool SingleVerDataSync::IsPermitLocalDeviceRecvData(const std::string &deviceId, + const SecurityOption &remoteSecOption) const +{ + return RuntimeContext::GetInstance()->CheckDeviceSecurityAbility(deviceId, remoteSecOption); +} + +int SingleVerDataSync::CheckPermitSendData(int mode, SingleVerSyncTaskContext *context) +{ + uint32_t version = std::min(context->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT); + // for pull mode it just need to get data, no need to send data. + if (version <= SOFTWARE_VERSION_RELEASE_2_0 || mode == SyncOperation::PULL) { + return E_OK; + } + if (context->GetSendPermitCheck()) { + return E_OK; + } + bool isPermitSync = true; + std::string deviceId = context->GetDeviceId(); + SecurityOption remoteSecOption = context->GetRemoteSeccurityOption(); + if (mode == SyncOperation::PUSH || mode == SyncOperation::PUSH_AND_PULL || mode == SyncOperation::RESPONSE_PULL) { + isPermitSync = IsPermitRemoteDeviceRecvData(deviceId, remoteSecOption); + } + LOGI("[DataSync][PermitSendData] mode=%d,dev=%s{private},label=%d,flag=%d,PermitSync=%d", mode, deviceId.c_str(), + remoteSecOption.securityLabel, remoteSecOption.securityFlag, isPermitSync); + if (isPermitSync) { + context->SetSendPermitCheck(true); + return E_OK; + } + if (mode == SyncOperation::PUSH || mode == SyncOperation::PUSH_AND_PULL) { + context->SetTaskErrCode(-E_SECURITY_OPTION_CHECK_ERROR); + return -E_SECURITY_OPTION_CHECK_ERROR; + } + if (mode == SyncOperation::RESPONSE_PULL) { + std::vector outData; + SendPullResponseDataPkt(-E_SECURITY_OPTION_CHECK_ERROR, outData, context); + 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::CheckPermitReceiveData(const SingleVerSyncTaskContext *context) +{ + SecurityOption remoteSecOption = context->GetRemoteSeccurityOption(); + std::string localDeviceId; + if (communicateHandle_ == nullptr || remoteSecOption.securityLabel == NOT_SURPPORT_SEC_CLASSIFICATION) { + return true; + } + communicateHandle_->GetLocalIdentity(localDeviceId); + bool isPermitSync = IsPermitLocalDeviceRecvData(localDeviceId, remoteSecOption); + if (isPermitSync) { + return isPermitSync; + } + LOGE("[DataSync][PermitReceiveData] check failed: permitReceive=%d, localDev=%s{private}, seclabel=%d, secflag=%d", + isPermitSync, localDeviceId.c_str(), remoteSecOption.securityLabel, remoteSecOption.securityFlag); + return isPermitSync; +} + +int SingleVerDataSync::CheckSchemaStrategy(SingleVerSyncTaskContext *context, const Message *message) +{ + SyncStrategy localStrategy = context->GetSyncStrategy(); + if (!context->GetIsSchemaSync()) { + LOGE("[DataSync][CheckSchemaStrategy] isSchemaSync=%d check failed", context->GetIsSchemaSync()); + (void)SendAck(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)SendAck(context, message, -E_SCHEMA_MISMATCH, 0); + return -E_SCHEMA_MISMATCH; + } + return E_OK; +} + +void SingleVerDataSync::RemotePushFinished(int sendCode, int mode, uint32_t msgSessionId, uint32_t contextSessionId) +{ + if ((mode != SyncOperation::PUSH) && (mode != SyncOperation::PUSH_AND_PULL)) { + return; + } + + if ((sendCode == E_OK) && (msgSessionId != 0) && (msgSessionId != contextSessionId)) { + storage_->NotifyRemotePushFinished(deviceId_); + } +} + +void SingleVerDataSync::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); + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.h new file mode 100755 index 000000000..1aee6bfb3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "meta_data.h" +#include "single_ver_kvdb_sync_interface.h" +#include "single_ver_sync_task_context.h" +#include "sync_types.h" +#include "version.h" +#include "parcel.h" + +namespace DistributedDB { +using SendDataItem = SingleVerKvEntry *; + +struct DataSyncReSendInfo { + uint32_t sessionId = 0; + uint32_t sequenceId = 0; + TimeStamp start = 0; // means localwatermark + TimeStamp end = 0; + uint64_t packetId = 0; +}; + +class DataRequestPacket { +public: + DataRequestPacket() {}; + ~DataRequestPacket(); + + void SetData(std::vector &data); + + const std::vector &GetData() 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() const; + + 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(); + + void SetBasicInfo(int sendCode, uint32_t version, WaterMark localMark, WaterMark peerMark, int32_t mode); + +private: + std::vector data_; + WaterMark endWaterMark_ = 0; + WaterMark localWaterMark_ = 0; + WaterMark peerWaterMark_ = 0; + int32_t sendCode_ = 0; + int32_t mode_ = SyncOperation::INVALID; + uint32_t sessionId_ = 0; + uint32_t version_ = SOFTWARE_VERSION_CURRENT; + std::vector reserved_; + uint32_t flag_ = 0; // bit 0 used for isLastSequence + static const uint32_t IS_LAST_SEQUENCE = 0x1; // bit 0 used for isLastSequence, 1: is last, 0: not last +}; + +class DataAckPacket { +public: + DataAckPacket() {}; + ~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 SingleVerDataSync { +public: + SingleVerDataSync(); + ~SingleVerDataSync(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerDataSync); + + 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(); + + int Initialize(IKvDBSyncInterface *inStorage, ICommunicator *inCommunicateHandle, + std::shared_ptr &inMetadata, const std::string &deviceId); + + int PushStart(SingleVerSyncTaskContext *context); + + int PushPullStart(SingleVerSyncTaskContext *context); + + int PullRequestStart(SingleVerSyncTaskContext *context); + + int PullResponseStart(SingleVerSyncTaskContext *context); + + int RequestRecv(SingleVerSyncTaskContext *context, const Message *message, WaterMark &pullEndWatermark); + + int AckRecv(SingleVerSyncTaskContext *context, const Message *message); + + void SendSaveDataNotifyPacket(SingleVerSyncTaskContext *context, uint32_t pktVersion, uint32_t sessionId, + uint32_t sequenceId); + + void SendAck(SingleVerSyncTaskContext *context, uint32_t sessionId, uint32_t sequenceId, uint64_t packetId); + + int32_t ReSend(SingleVerSyncTaskContext *context, DataSyncReSendInfo reSendInfo); + + int CheckPermitSendData(int mode, SingleVerSyncTaskContext *context); + + std::string GetLabel() const; + + std::string GetDeviceId() const; + +private: + 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 + + static int AckPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int DataPacketCalculateLen(const Message *inMsg, uint32_t &len); + + static int DataPacketSerialization(uint8_t *buffer, uint32_t length, const Message *inMsg); + + static int DataPacketDeSerialization(const uint8_t *buffer, uint32_t length, Message *inMsg); + + 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); + + static TimeStamp GetMaxSendDataTime(const std::vector &inData, bool isNeedInit, + WaterMark localMark = 0); + + static TimeStamp GetMinSendDataTime(const std::vector &inData, WaterMark localMark); + + int GetData(SingleVerSyncTaskContext *context, std::vector &outData, size_t packetSize); + + int GetDataWithRerformanceRecord(SingleVerSyncTaskContext *context, std::vector &outData); + + 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); + + int SaveLocalWaterMark(const DeviceID &deviceId, WaterMark waterMark); + + int SavePeerWaterMark(const DeviceID &deviceId, WaterMark waterMark); + + int RemoveDeviceData(SingleVerSyncTaskContext *context, const Message *message, WaterMark maxSendDataTime); + + void TransSendDataItemToLocal(const SingleVerSyncTaskContext *context, + const std::vector &data); + + void TransDbDataItemToSendDataItem(const SingleVerSyncTaskContext *context, + std::vector &outData); + + int SendDataPacket(const DataRequestPacket *packet, SingleVerSyncTaskContext *context); + + void SetAckData(DataAckPacket &ackPacket, SingleVerSyncTaskContext *context, int32_t recvCode, + WaterMark maxSendDataTime) const; + + int SendAck(SingleVerSyncTaskContext *context, const Message *message, int32_t recvCode, + WaterMark maxSendDataTime); + + int SendLocalWaterMarkAck(SingleVerSyncTaskContext *context, const Message *message); + + void UpdatePeerWaterMark(const SingleVerSyncTaskContext *context, WaterMark peerWatermark); + + std::string GetLocalDeviceName(); + + std::string TransferForeignOrigDevName(const std::string &deviceName); + + std::string TransferLocalOrigDevName(const std::string &origName); + + int RequestRecvPre(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); + + static int DataPacketSyncerPartSerialization(Parcel &parcel, const DataRequestPacket *packet); + + static int DataPacketSyncerPartDeSerialization(Parcel &parcel, DataRequestPacket *packet, uint32_t packLen, + uint32_t length, uint32_t version); + + static int AckPacketSyncerPartSerializationV1(Parcel &parcel, const DataAckPacket *packet); + + static int AckPacketSyncerPartDeSerializationV1(Parcel &parcel, DataAckPacket &packet); + + 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, std::vector &inData, + SingleVerSyncTaskContext *context); + + void SetPacketId(DataRequestPacket *packet, SingleVerSyncTaskContext *context, uint32_t version); + + bool IsPermitRemoteDeviceRecvData(const std::string &deviceId, const SecurityOption &secOption) const; + + bool IsPermitLocalDeviceRecvData(const std::string &deviceId, const SecurityOption &remoteSecOption) const; + + bool CheckPermitReceiveData(const SingleVerSyncTaskContext *context); + + int CheckSchemaStrategy(SingleVerSyncTaskContext *context, const Message *message); + + void RemotePushFinished(int sendCode, int mode, uint32_t msgSessionId, uint32_t contetSessionId); + + uint32_t mtuSize_; + SingleVerKvDBSyncInterface* storage_; + ICommunicator* communicateHandle_; + std::shared_ptr metadata_; + std::string label_; + std::string deviceId_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_DATA_SYNC_NEW_H + diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.cpp new file mode 100755 index 000000000..4b9926ac6 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.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 "single_ver_data_sync_with_sliding_window.h" + +namespace DistributedDB { +int SingleVerDataSyncWithSlidingWindow::SenderStart(int32_t mode, SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + return sender_.SendStart(mode, context, dataSync); +} + +int SingleVerDataSyncWithSlidingWindow::SenderAckRecv(const Message *message) +{ + return sender_.AckRecv(message); +} + +int SingleVerDataSyncWithSlidingWindow::PreHandleSenderAckRecv(const Message *message) +{ + return sender_.PreHandleAckRecv(message); +} + +void SingleVerDataSyncWithSlidingWindow::SenderClear() +{ + sender_.Clear(); +} + +void SingleVerDataSyncWithSlidingWindow::SetSenderErr(bool isErr) +{ + sender_.SetErr(isErr); +} + +int SingleVerDataSyncWithSlidingWindow::ReceiverInit(SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + return receiver_.Initialize(context, dataSync); +} + +int SingleVerDataSyncWithSlidingWindow::SenderInit(SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + return sender_.Initialize(context, dataSync); +} + +void SingleVerDataSyncWithSlidingWindow::ReceiverClear() +{ + receiver_.Clear(); +} + +int SingleVerDataSyncWithSlidingWindow::Receive(Message *inMsg) +{ + return receiver_.Receive(inMsg); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.h new file mode 100755 index 000000000..0c18c9bde --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_data_sync_with_sliding_window.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 SINGLE_VER_DATA_SYNC_WITH_SLIDING_WINDOW_H +#define SINGLE_VER_DATA_SYNC_WITH_SLIDING_WINDOW_H + +#include "sliding_window_sender.h" +#include "sliding_window_receiver.h" + +namespace DistributedDB { +class SingleVerDataSyncWithSlidingWindow { +public: + SingleVerDataSyncWithSlidingWindow() = default; + ~SingleVerDataSyncWithSlidingWindow() = default; + DISABLE_COPY_ASSIGN_MOVE(SingleVerDataSyncWithSlidingWindow); + + int SenderStart(int32_t mode, SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + int PreHandleSenderAckRecv(const Message *message); + int SenderAckRecv(const Message *message); + void SenderClear(); + void SetSenderErr(bool isErr); + + int ReceiverInit(SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + int SenderInit(SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + void ReceiverClear(); + int Receive(Message *inMsg); +private: + SlidingWindowSender sender_; + SlidingWindowReceiver receiver_; +}; +} // namespace DistributedDB +#endif // SINGLE_VER_DATA_SYNC_WITH_SLIDING_WINDOW_H + diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.cpp new file mode 100755 index 000000000..1c332d388 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.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 "single_ver_sync_engine.h" +#include "single_ver_sync_task_context.h" +#include "log_print.h" + +namespace DistributedDB { +ISyncTaskContext *SingleVerSyncEngine::CreateSyncTaskContext() +{ + auto context = new (std::nothrow) SingleVerSyncTaskContext; + if (context == nullptr) { + LOGE("[SingleVerSyncEngine][CreateSyncTaskContext] create failed, may be out of memory"); + return nullptr; + } + context->EnableClearRemoteStaleData(needClearRemoteStaleData_); + 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); + } + } +} + +DEFINE_OBJECT_TAG_FACILITIES(SingleVerSyncEngine); +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.h new file mode 100644 index 000000000..c4d4ea04f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_engine.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 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 rebuiled. + void EnableClearRemoteStaleData(bool enable); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSyncEngine); +protected: + ~SingleVerSyncEngine() override {}; + + // Create a context + ISyncTaskContext *CreateSyncTaskContext() override; + +private: + DECLARE_OBJECT_TAG(SingleVerSyncEngine); + + bool needClearRemoteStaleData_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_ENGINE_H \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.cpp new file mode 100755 index 000000000..adc30e2d2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.cpp @@ -0,0 +1,1140 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "runtime_context.h" +#include "performance_analysis.h" +#include "single_ver_sync_target.h" + +namespace DistributedDB { +namespace { + // used for state switch table + const int CURRENT_STATE_INDEX = 0; + const int EVENT_INDEX = 1; + const int OUTPUT_STATE_INDEX = 2; + + // State switch table v1 and v2, has three columns, CurrentState, Event, and OutSate + const std::vector> STATE_SWITCH_TABLE_V2 = { + {IDLE, START_SYNC_EVENT, TIME_SYNC}, + + // In TIME_SYNC state + {TIME_SYNC, TIME_SYNC_FINISHED_EVENT, ABILITY_SYNC}, + {TIME_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {TIME_SYNC, INNER_ERR_EVENT, INNER_ERR}, + + // In ABILITY_SYNC state, compare version num and schema + {ABILITY_SYNC, VERSION_NOT_SUPPORT_EVENT, INNER_ERR}, + {ABILITY_SYNC, SWITCH_TO_PROCTOL_V1_EVENT, START_INITIACTIVE_DATA_SYNC}, + {ABILITY_SYNC, ABILITY_SYNC_FINISHED_EVENT, START_INITIACTIVE_DATA_SYNC}, + {ABILITY_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {ABILITY_SYNC, INNER_ERR_EVENT, INNER_ERR}, + + // In START_INITIACTIVE_DATA_SYNC state, send a sync request, and send first packet of data sync + {START_INITIACTIVE_DATA_SYNC, SEND_DATA_EVENT, INACTIVE_PUSH_REMAINDER_DATA}, + {START_INITIACTIVE_DATA_SYNC, RESPONSE_PUSH_REMAINDER_EVENT, PASSIVE_PUSH_REMAINDER_DATA}, + {START_INITIACTIVE_DATA_SYNC, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + {START_INITIACTIVE_DATA_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {START_INITIACTIVE_DATA_SYNC, INNER_ERR_EVENT, INNER_ERR}, + {START_INITIACTIVE_DATA_SYNC, RE_SEND_DATA_EVENT, START_INITIACTIVE_DATA_SYNC}, + {START_INITIACTIVE_DATA_SYNC, SEND_FINISHED_EVENT, START_PASSIVE_DATA_SYNC}, + + // In INACTIVE_PUSH_REMAINDER_DATA state, do initiactive sync, send remainder pcket of data sync + {INACTIVE_PUSH_REMAINDER_DATA, SEND_DATA_EVENT, INACTIVE_PUSH_REMAINDER_DATA}, + {INACTIVE_PUSH_REMAINDER_DATA, SEND_FINISHED_EVENT, START_PASSIVE_DATA_SYNC}, + {INACTIVE_PUSH_REMAINDER_DATA, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {INACTIVE_PUSH_REMAINDER_DATA, INNER_ERR_EVENT, INNER_ERR}, + {INACTIVE_PUSH_REMAINDER_DATA, RE_SEND_DATA_EVENT, INACTIVE_PUSH_REMAINDER_DATA}, + {INACTIVE_PUSH_REMAINDER_DATA, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + + // In START_PASSIVE_DATA_SYNC state, do response pull request, and send first packet of data sync + {START_PASSIVE_DATA_SYNC, SEND_DATA_EVENT, PASSIVE_PUSH_REMAINDER_DATA}, + {START_PASSIVE_DATA_SYNC, SEND_FINISHED_EVENT, START_PASSIVE_DATA_SYNC}, + {START_PASSIVE_DATA_SYNC, RESPONSE_TASK_FINISHED_EVENT, WAIT_FOR_RECEIVE_DATA_FINISH}, + {START_PASSIVE_DATA_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {START_PASSIVE_DATA_SYNC, INNER_ERR_EVENT, INNER_ERR}, + {START_PASSIVE_DATA_SYNC, RE_SEND_DATA_EVENT, PASSIVE_PUSH_REMAINDER_DATA}, + {START_PASSIVE_DATA_SYNC, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + + // In PASSIVE_PUSH_REMAINDER_DATA state, do passive sync, send remainder pcket of data sync + {PASSIVE_PUSH_REMAINDER_DATA, SEND_DATA_EVENT, PASSIVE_PUSH_REMAINDER_DATA}, + {PASSIVE_PUSH_REMAINDER_DATA, SEND_FINISHED_EVENT, START_PASSIVE_DATA_SYNC}, + {PASSIVE_PUSH_REMAINDER_DATA, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {PASSIVE_PUSH_REMAINDER_DATA, INNER_ERR_EVENT, INNER_ERR}, + {PASSIVE_PUSH_REMAINDER_DATA, RE_SEND_DATA_EVENT, PASSIVE_PUSH_REMAINDER_DATA}, + {PASSIVE_PUSH_REMAINDER_DATA, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + + // In WAIT_FOR_RECEIVE_DATA_FINISH, + {WAIT_FOR_RECEIVE_DATA_FINISH, RECV_FINISHED_EVENT, SYNC_TASK_FINISHED}, + {WAIT_FOR_RECEIVE_DATA_FINISH, START_PULL_RESPONSE_EVENT, START_PASSIVE_DATA_SYNC}, + {WAIT_FOR_RECEIVE_DATA_FINISH, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {WAIT_FOR_RECEIVE_DATA_FINISH, INNER_ERR_EVENT, INNER_ERR}, + {WAIT_FOR_RECEIVE_DATA_FINISH, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + + // In SYNC_TASK_FINISHED, + {SYNC_TASK_FINISHED, ALL_TASK_FINISHED_EVENT, IDLE}, + {SYNC_TASK_FINISHED, START_SYNC_EVENT, TIME_SYNC}, + + // SYNC_TIME_OUT and INNE_ERR state, just do some exception resolve + {SYNC_TIME_OUT, ANY_EVENT, SYNC_TASK_FINISHED}, + {INNER_ERR, ANY_EVENT, SYNC_TASK_FINISHED}, + }; + + // State switch table v3, has three columns, CurrentState, Event, and OutSate + const std::vector> STATE_SWITCH_TABLE_V3 = { + {IDLE, START_SYNC_EVENT, TIME_SYNC}, + + // In TIME_SYNC state + {TIME_SYNC, TIME_SYNC_FINISHED_EVENT, ABILITY_SYNC}, + {TIME_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {TIME_SYNC, INNER_ERR_EVENT, INNER_ERR}, + + // In ABILITY_SYNC state, compare version num and schema + {ABILITY_SYNC, VERSION_NOT_SUPPORT_EVENT, INNER_ERR}, + {ABILITY_SYNC, ABILITY_SYNC_FINISHED_EVENT, START_INITIACTIVE_SLIDING_DATA_SYNC}, + {ABILITY_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {ABILITY_SYNC, INNER_ERR_EVENT, INNER_ERR}, + + // In START_INITIACTIVE_SLIDING_DATA_SYNC state, send a sync request, and send first packet of data sync + {START_INITIACTIVE_SLIDING_DATA_SYNC, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + {START_INITIACTIVE_SLIDING_DATA_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {START_INITIACTIVE_SLIDING_DATA_SYNC, INNER_ERR_EVENT, INNER_ERR}, + {START_INITIACTIVE_SLIDING_DATA_SYNC, SEND_FINISHED_EVENT, START_PASSIVE_SLIDING_DATA_SYNC}, + {START_INITIACTIVE_SLIDING_DATA_SYNC, RE_SEND_DATA_EVENT, START_INITIACTIVE_SLIDING_DATA_SYNC}, + + // In START_PASSIVE_SLIDING_DATA_SYNC state, do response pull request, and send first packet of data sync + {START_PASSIVE_SLIDING_DATA_SYNC, SEND_FINISHED_EVENT, START_PASSIVE_SLIDING_DATA_SYNC}, + {START_PASSIVE_SLIDING_DATA_SYNC, RESPONSE_TASK_FINISHED_EVENT, WAIT_FOR_RECEIVE_DATA_FINISH}, + {START_PASSIVE_SLIDING_DATA_SYNC, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {START_PASSIVE_SLIDING_DATA_SYNC, INNER_ERR_EVENT, INNER_ERR}, + {START_PASSIVE_SLIDING_DATA_SYNC, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + {START_PASSIVE_SLIDING_DATA_SYNC, RE_SEND_DATA_EVENT, START_PASSIVE_SLIDING_DATA_SYNC}, + + // In WAIT_FOR_RECEIVE_DATA_FINISH, + {WAIT_FOR_RECEIVE_DATA_FINISH, RECV_FINISHED_EVENT, SYNC_TASK_FINISHED}, + {WAIT_FOR_RECEIVE_DATA_FINISH, START_PULL_RESPONSE_EVENT, START_PASSIVE_SLIDING_DATA_SYNC}, + {WAIT_FOR_RECEIVE_DATA_FINISH, TIME_OUT_EVENT, SYNC_TIME_OUT}, + {WAIT_FOR_RECEIVE_DATA_FINISH, INNER_ERR_EVENT, INNER_ERR}, + {WAIT_FOR_RECEIVE_DATA_FINISH, NEED_ABILITY_SYNC_EVENT, ABILITY_SYNC}, + + // In SYNC_TASK_FINISHED, + {SYNC_TASK_FINISHED, ALL_TASK_FINISHED_EVENT, IDLE}, + {SYNC_TASK_FINISHED, START_SYNC_EVENT, TIME_SYNC}, + + // SYNC_TIME_OUT and INNE_ERR state, just do some exception resolve + {SYNC_TIME_OUT, ANY_EVENT, SYNC_TASK_FINISHED}, + {INNER_ERR, ANY_EVENT, 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, IKvDBSyncInterface *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(); + dataSyncWithSlidingWindow_ = 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, context->GetDeviceId()); + if (errCode != E_OK) { + goto ERROR_OUT; + } + + currentState_ = IDLE; + context_ = static_cast(context); + syncInterface_ = static_cast(syncInterface); + errCode = dataSyncWithSlidingWindow_->ReceiverInit(context_, dataSync_); + if (errCode != E_OK) { + goto ERROR_OUT; + } + errCode = dataSyncWithSlidingWindow_->SenderInit(context_, dataSync_); + if (errCode != E_OK) { + goto ERROR_OUT; + } + 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: + errCode = DataPktRecv(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{private}", currentState_, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + 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{private}", dataSync_->GetLabel().c_str(), + context_->GetDeviceId().c_str()); + if (context_->GetMode() == SyncOperation::PUSH_AND_PULL || context_->GetMode() == SyncOperation::PULL || + context_->IsKilled()) { + dataSyncWithSlidingWindow_->ReceiverClear(); + } + dataSyncWithSlidingWindow_->SenderClear(); + 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=%d,dev=%s{private}", + currentState_, context_->GetDeviceId().c_str()); + currentState_ = State::IDLE; + } + return E_OK; +} + +void SingleVerSyncStateMachine::SendSaveDataNotifyPacket(uint32_t sessionId, uint32_t sequenceId) +{ + dataSync_->SendSaveDataNotifyPacket(context_, + std::min(context_->GetRemoteSoftwareVersion(), SOFTWARE_VERSION_CURRENT), sessionId, sequenceId); +} + +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_V2, STATE_SWITCH_TABLE_V2); + 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=%llu", 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_[START_INITIACTIVE_DATA_SYNC] = std::bind(&SingleVerSyncStateMachine::DoInitiactiveDataSync, + this); + stateMapping_[START_PASSIVE_DATA_SYNC] = std::bind(&SingleVerSyncStateMachine::DoPassiveDataSync, this); + stateMapping_[INACTIVE_PUSH_REMAINDER_DATA] = std::bind(&SingleVerSyncStateMachine::DoInitiactivePushRemainderData, + this); + stateMapping_[PASSIVE_PUSH_REMAINDER_DATA] = std::bind(&SingleVerSyncStateMachine::DoPassivePushRemainderData, + 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_SLIDING_DATA_SYNC] = + std::bind(&SingleVerSyncStateMachine::DoInitiactiveDataSyncWithSlidingWindow, this); + stateMapping_[START_PASSIVE_SLIDING_DATA_SYNC] = + std::bind(&SingleVerSyncStateMachine::DoPassiveDataSyncWithSlidingWindow, this); +} + +Event SingleVerSyncStateMachine::DoInitiactiveDataSync() +{ + int errCode = E_OK; + switch (context_->GetMode()) { + case SyncOperation::PUSH: + context_->SetOperationStatus(SyncOperation::RECV_FINISHED); + errCode = dataSync_->PushStart(context_); + break; + case SyncOperation::PULL: + context_->SetOperationStatus(SyncOperation::SEND_FINISHED); + errCode = dataSync_->PullRequestStart(context_); + break; + case SyncOperation::PUSH_AND_PULL: + errCode = dataSync_->PushPullStart(context_); + break; + case SyncOperation::RESPONSE_PULL: + // In response pull mode, reminader data should send in + // PASSIVE_PUSH_REMAINDER_DATA + return Event::RESPONSE_PUSH_REMAINDER_EVENT; + default: + errCode = -E_NOT_SUPPORT; + break; + } + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoInitiactiveDataSyncWithSlidingWindow() +{ + LOGD("[StateMachine][activeDataSync] mode=%d,label=%s,dev=%s{private}", context_->GetMode(), + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + int errCode = E_OK; + switch (context_->GetMode()) { + case SyncOperation::PUSH: + context_->SetOperationStatus(SyncOperation::RECV_FINISHED); + errCode = dataSyncWithSlidingWindow_->SenderStart(SyncOperation::PUSH, context_, dataSync_); + break; + case SyncOperation::PULL: + context_->SetOperationStatus(SyncOperation::SEND_FINISHED); + errCode = dataSyncWithSlidingWindow_->SenderStart(SyncOperation::PULL, context_, dataSync_); + break; + case SyncOperation::PUSH_AND_PULL: + errCode = dataSyncWithSlidingWindow_->SenderStart(SyncOperation::PUSH_AND_PULL, context_, dataSync_); + break; + case SyncOperation::RESPONSE_PULL: + errCode = dataSyncWithSlidingWindow_->SenderStart(SyncOperation::RESPONSE_PULL, context_, dataSync_); + 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 (context_->GetMode() == SyncOperation::PUSH_AND_PULL && errCode == -E_EKEYREVOKED) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoPassiveDataSync() +{ + { + RefObject::AutoLock lock(context_); + if (context_->GetRspTargetQueueSize() != 0) { + PreStartPullResponse(); + } else { + return RESPONSE_TASK_FINISHED_EVENT; + } + } + int errCode = dataSync_->PullResponseStart(context_); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoPassiveDataSyncWithSlidingWindow() +{ + { + RefObject::AutoLock lock(context_); + if (context_->GetRspTargetQueueSize() != 0) { + PreStartPullResponse(); + } else { + return RESPONSE_TASK_FINISHED_EVENT; + } + } + int errCode = dataSyncWithSlidingWindow_->SenderStart(SyncOperation::RESPONSE_PULL, context_, dataSync_); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoInitiactivePushRemainderData() +{ + int errCode; + switch (context_->GetMode()) { + case SyncOperation::PULL: + case SyncOperation::RESPONSE_PULL: + // In pull or response pul mode, don't need to do INACTIVE_PUSH + return Event::SEND_FINISHED_EVENT; + case SyncOperation::PUSH: + case SyncOperation::PUSH_AND_PULL: + errCode = dataSync_->PushStart(context_); + break; + default: + errCode = -E_INTERNAL_ERROR; + break; + } + + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoPassivePushRemainderData() +{ + int errCode = dataSync_->PullResponseStart(context_); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + return TransformErrCodeToEvent(errCode); +} + +Event SingleVerSyncStateMachine::DoWaitForDataRecv() const +{ + if (context_->GetRspTargetQueueSize() != 0) { + return START_PULL_RESPONSE_EVENT; + } + if (context_->GetOperationStatus() == SyncOperation::FINISHED_ALL) { + return RECV_FINISHED_EVENT; + } + if (context_->GetMode() == SyncOperation::PUSH_AND_PULL && + context_->GetOperationStatus() == SyncOperation::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); + if (errCode == E_OK) { + return Event::WAIT_ACK_EVENT; + } + 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); + LOGI("remote version is 0, switch to v1 proctol"); + return Event::SWITCH_TO_PROCTOL_V1_EVENT; + } + + if (abilitySync_->GetAbilitySyncFinishedStatus()) { + return Event::ABILITY_SYNC_FINISHED_EVENT; + } + + CommErrHandler handler = std::bind(&SyncTaskContext::CommErrHandlerFunc, std::placeholders::_1, + context_, context_->GetRequestSessionId()); + LOGI("[StateMachine][AbilitySync] start abilitySync,label=%s,dev=%s{private}", dataSync_->GetLabel().c_str(), + context_->GetDeviceId().c_str()); + 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::DoSyncTaskFinished() +{ + StopWatchDog(); + dataSyncWithSlidingWindow_->SenderClear(); + 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_); + context_->Abort(SyncOperation::TIMEOUT); + context_->Clear(); + AbortInner(); + return Event::ANY_EVENT; +} + +Event SingleVerSyncStateMachine::DoInnerErr() +{ + RefObject::AutoLock lock(context_); + if (!context_->IsCommNormal()) { + context_->Abort(SyncOperation::COMM_ABNORMAL); + } else if (context_->GetTaskErrCode() == -E_SCHEMA_MISMATCH) { + context_->Abort(SyncOperation::SCHEMA_INCOMPATIBLE); + } else if (context_->GetTaskErrCode() == -E_EKEYREVOKED) { + context_->Abort(SyncOperation::EKEYREVOKED_FAILURE); + } else if (context_->GetTaskErrCode() == -E_SECURITY_OPTION_CHECK_ERROR) { + context_->Abort(SyncOperation::SECURITY_OPTION_CHECK_FAILURE); + } else if (context_->GetTaskErrCode() == -E_BUSY) { + context_->Abort(SyncOperation::BUSY_FAILURE); + } else { + context_->Abort(SyncOperation::FAILED); + } + 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) { + int errCode = abilitySync_->AckRecv(inMsg, context_); + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + if (errCode != E_OK && errCode != E_FEEDBACK_UNKNOWN_MESSAGE) { + 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{private}", + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + SwitchStateAndStep(ABILITY_SYNC_FINISHED_EVENT); + } + 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) { + abilitySync_->SetAbilitySyncFinishedStatus(true); + LOGI("[StateMachine][AbilitySyncRecv] ability sync finished,label=%s,dev=%s{private}", + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + (static_cast(context_))->SetIsSchemaSync(true); + std::lock_guard lock(stateMachineLock_); + SwitchStateAndStep(ABILITY_SYNC_FINISHED_EVENT); + } else { + abilitySync_->AckNotifyRecv(inMsg, context_); + } + return E_OK; + } + + LOGE("[StateMachine][AbilitySyncRecv] msg type invalid"); + return -E_NOT_SUPPORT; +} + +int SingleVerSyncStateMachine::HandleDataRequestRecv(const Message *inMsg) +{ + StopFeedDogForSync(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()); + WaterMark pullEndWaterkark = 0; + int errCode = dataSync_->RequestRecv(context_, inMsg, pullEndWaterkark); + 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_); + if (IsNeedErrCodeHandle(inMsg->GetSessionId())) { + switch (errCode) { + case E_OK: + break; + case -E_RECV_FINISHED: + context_->SetOperationStatus(SyncOperation::RECV_FINISHED); + SwitchStateAndStep(Event::RECV_FINISHED_EVENT); + break; + case -E_EKEYREVOKED: + PushPullDataRequestEvokeErrHandle(context_); + break; + case -E_BUSY: + context_->SetTaskErrCode(-E_BUSY); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + break; + case -E_SECURITY_OPTION_CHECK_ERROR: + context_->SetTaskErrCode(-E_SECURITY_OPTION_CHECK_ERROR); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + break; + case -E_NEED_ABILITY_SYNC: + return errCode; + default: + SwitchStateAndStep(Event::INNER_ERR_EVENT); + break; + } + } + if (pullEndWaterkark > 0) { + AddPullResponseTarget(pullEndWaterkark, inMsg->GetSessionId()); + } + return E_OK; +} + +int SingleVerSyncStateMachine::PreHandleAckRecv(const Message *inMsg) +{ + if (context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + return dataSyncWithSlidingWindow_->PreHandleSenderAckRecv(inMsg); + } + return E_OK; +} + +void SingleVerSyncStateMachine::HandleDataAckRecvWithSlidingWindow(int errCode, const Message *inMsg) +{ + if (errCode == -E_RE_SEND_DATA) { // LOCAL_WATER_MARK_NOT_INIT + dataSyncWithSlidingWindow_->SenderClear(); + } + if (errCode == -E_NO_DATA_SEND || errCode == -E_SEND_DATA) { + int ret = dataSyncWithSlidingWindow_->SenderAckRecv(inMsg); + if (ret == -E_FINISHED) { + SwitchStateAndStep(Event::SEND_FINISHED_EVENT); + } else if (ret != E_OK) { + SwitchStateAndStep(TransformErrCodeToEvent(ret)); + } + } else { + SwitchStateAndStep(TransformErrCodeToEvent(errCode)); + } +} + +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_ = %llu", currentRemoteVersionId_); + context_->SetRemoteSoftwareVersion(0); + } else { + currentRemoteVersionId_ = context_->GetRemoteSoftwareVersionId(); + } + abilitySync_->SetAbilitySyncFinishedStatus(false); + dataSyncWithSlidingWindow_->SenderClear(); +} + +int SingleVerSyncStateMachine::HandleDataAckRecv(const Message *inMsg) +{ + StopFeedDogForSync(SyncDirectionFlag::SEND); + std::lock_guard lock(stateMachineLock_); + if (IsNeedResetWatchdog(inMsg)) { + (void)ResetWatchDog(); + } + int errCode = PreHandleAckRecv(inMsg); + if (errCode != E_OK) { + // 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. + if (errCode == -E_SLIDING_WINDOW_RECEIVER_INVALID_MSG) { + return E_OK; + } + // this means error happened,should stop the sync task. + SwitchStateAndStep(TransformErrCodeToEvent(errCode)); + return errCode; + } + // 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()); + errCode = dataSync_->AckRecv(context_, inMsg); + if (isNeedStop) { + StopSaveDataNotify(); + } + + switch (errCode) { + case -E_NEED_ABILITY_SYNC: + NeedAbilitySyncHandle(); + break; + case -E_NO_DATA_SEND: + // when version is higher than 102, this step will be handle in slide windows function. + if (context_->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + context_->SetOperationStatus(SyncOperation::SEND_FINISHED); + } + break; + case -E_NOT_PERMIT: + context_->SetOperationStatus(SyncOperation::PERMISSION_CHECK_FAILED); + break; + case -E_EKEYREVOKED: + case -E_SECURITY_OPTION_CHECK_ERROR: + case -E_BUSY: + context_->SetTaskErrCode(errCode); + break; + case -E_SAVE_DATA_NOTIFY: + return errCode; + default: + break; + } + if (context_->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + SwitchStateAndStep(TransformErrCodeToEvent(errCode)); + return errCode; + } + HandleDataAckRecvWithSlidingWindow(errCode, inMsg); + return errCode; +} + +int SingleVerSyncStateMachine::DataPktRecv(Message *inMsg) +{ + PerformanceAnalysis *performance = PerformanceAnalysis::GetInstance(); + int errCode; + TimeOffset offset = 0; + + switch (inMsg->GetMessageType()) { + case TYPE_REQUEST: + // If message is data sync request, we should check timeoffset. + errCode = timeSync_->GetTimeOffset(offset); + if (errCode != E_OK) { + LOGE("[StateMachine][DataPktRecv] GetTimeOffset err! errCode=%d", errCode); + return errCode; + } + context_->SetTimeOffset(offset); + if (performance != nullptr) { + performance->StepTimeRecordStart(PT_TEST_RECORDS::RECORD_DATA_REQUEST_RECV_TO_SEND_ACK); + } + if (context_->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + // higher than 102 version may go to here, but it is ok not use slwr,after abilitysync would go slwr. + errCode = HandleDataRequestRecv(inMsg); + } else { + errCode = dataSyncWithSlidingWindow_->Receive(inMsg); + } + 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::StepToTimeout() +{ + std::lock_guard lock(stateMachineLock_); + SwitchStateAndStep(Event::TIME_OUT_EVENT); +} + +int SingleVerSyncStateMachine::TimeMarkSyncRecv(const Message *inMsg) +{ + LOGD("[StateMachine][TimeMarkSyncRecv] type=%d,label=%s,dev=%s{private}", inMsg->GetMessageType(), + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + { + 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); + if (errCode != E_OK) { + LOGE("[StateMachine][TimeMarkSyncRecv] AckRecv failed errCode=%d", errCode); + return errCode; + } + std::lock_guard lock(stateMachineLock_); + SwitchStateAndStep(TIME_SYNC_FINISHED_EVENT); + return E_OK; + } else { + return -E_INVALID_ARGS; + } +} + +void SingleVerSyncStateMachine::Clear() +{ + dataSyncWithSlidingWindow_ = nullptr; + 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() > ABILITY_SYNC_MESSAGE)) { + LOGE("[StateMachine][IsPacketValid] Message is invalid, id=%d", inMsg->GetMessageId()); + return false; + } + + if (inMsg->GetMessageId() == TIME_SYNC_MESSAGE || inMsg->GetMessageId() == ABILITY_SYNC_MESSAGE) { + return true; + } + if (context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + return true; + } + + if (inMsg->GetMessageType() == TYPE_RESPONSE) { + if ((inMsg->GetSequenceId() != context_->GetSequenceId()) || + ((inMsg->GetSessionId() != context_->GetRequestSessionId()) && + (inMsg->GetSessionId() != context_->GetResponseSessionId()))) { + LOGE("[StateMachine][IsPacketValid] Message is invalid,inMsg SequenceId=%d,seqId=%d,syncId=%d", + inMsg->GetSequenceId(), context_->GetSequenceId(), context_->GetSyncId()); + return false; + } + } + + return true; +} + +void SingleVerSyncStateMachine::PreStartPullResponse() +{ + SingleVerSyncTarget target; + context_->PopResponseTarget(target); + context_->SetEndMark(target.GetEndWaterMark()); + context_->SetResponseSessionId(target.GetResponseSessionId()); + context_->SetMode(SyncOperation::RESPONSE_PULL); + context_->ReSetSequenceId(); +} + +bool SingleVerSyncStateMachine::IsRightDataResponsePkt(const Message *inMsg) const +{ + if (inMsg->GetMessageId() != DATA_SYNC_MESSAGE) { + return false; + } + + if (context_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_RELEASE_2_0) { + return false; + } + + if ((context_->GetMode() != SyncOperation::INVALID) && (inMsg->GetMessageType() == TYPE_RESPONSE)) { + return true; + } + + return false; +} + +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; + } + + if (IsRightDataResponsePkt(inMsg)) { + context_->IncSequenceId(); + } + return E_OK; +} + +void SingleVerSyncStateMachine::AddPullResponseTarget(WaterMark pullEndWatermark, uint32_t sessionId) +{ + if (pullEndWatermark == 0) { + LOGE("[StateMachine][AddPullResponseTarget] pullEndWatermark is 0!"); + return; + } + SingleVerSyncTarget *targetTmp = new (std::nothrow) SingleVerSyncTarget; + if (targetTmp == nullptr) { + LOGE("[StateMachine][AddPullResponseTarget] add failed, may oom"); + return; + } + targetTmp->SetTaskType(ISyncTarget::RESPONSE); + targetTmp->SetMode(SyncOperation::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_SUPPORT_EVENT: + return Event::VERSION_NOT_SUPPORT_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_->IsAutoSync() && (syncContext_->GetRetryTime() < RETRY_TIME)) { + return Event::WAIT_TIME_OUT_EVENT; + } + return Event::TIME_OUT_EVENT; +} + +void SingleVerSyncStateMachine::SetSlidingWindowSenderErr(bool isErr) +{ + dataSyncWithSlidingWindow_->SetSenderErr(isErr); +} + +bool SingleVerSyncStateMachine::IsNeedErrCodeHandle(uint32_t sessionId) const +{ + // version_102 omit to set sessionId so version_3 should skip to compare sessionid. + if (sessionId == context_->GetRequestSessionId() || context_->GetRequestSessionId() == 0 || + context_->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_RELEASE_2_0) { + return true; + } else { + return false; + } +} + +void SingleVerSyncStateMachine::PushPullDataRequestEvokeErrHandle(SingleVerSyncTaskContext *context) +{ + // 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 && + context->GetMode() == SyncOperation::PUSH_AND_PULL) { + LOGI("data request errCode = %d, wait for send finished", -E_EKEYREVOKED); + context->SetTaskErrCode(-E_EKEYREVOKED); + context->SetOperationStatus(SyncOperation::RECV_FINISHED); + SwitchStateAndStep(Event::RECV_FINISHED_EVENT); + } else { + context->SetTaskErrCode(-E_EKEYREVOKED); + SwitchStateAndStep(Event::INNER_ERR_EVENT); + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.h new file mode 100755 index 000000000..27f69eba7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_state_machine.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sync_state_machine.h" +#include "sync_target.h" +#include "semaphore.h" +#include "message.h" +#include "single_ver_sync_task_context.h" +#include "time_sync.h" +#include "time_helper.h" +#include "single_ver_data_sync.h" +#include "meta_data.h" +#include "ability_sync.h" +#include "single_ver_data_sync_with_sliding_window.h" + +namespace DistributedDB { +namespace { + enum State { + IDLE = 0, + TIME_SYNC, + ABILITY_SYNC, + START_INITIACTIVE_DATA_SYNC, // used for do sync started by local device + START_PASSIVE_DATA_SYNC, // used for do sync response remote device + INACTIVE_PUSH_REMAINDER_DATA, // push remainded data if initactive sync data more than on frame + PASSIVE_PUSH_REMAINDER_DATA, // push remainded data if passive sync data more than on frame + WAIT_FOR_RECEIVE_DATA_FINISH, // all data send finished, wait for data revice if has pull request + SYNC_TASK_FINISHED, // current sync task finished, try to schedule next sync task + SYNC_TIME_OUT, + INNER_ERR, + START_INITIACTIVE_SLIDING_DATA_SYNC, // used for do sync started by local device, use sliding window + START_PASSIVE_SLIDING_DATA_SYNC // used for do pull response, use sliding window + }; + + enum Event { + START_SYNC_EVENT = 1, + TIME_SYNC_FINISHED_EVENT, + ABILITY_SYNC_FINISHED_EVENT, + VERSION_NOT_SUPPORT_EVENT, + SWITCH_TO_PROCTOL_V1_EVENT, + SEND_DATA_EVENT, + SEND_FINISHED_EVENT, + RECV_FINISHED_EVENT, + NEED_ABILITY_SYNC_EVENT, + RESPONSE_PUSH_REMAINDER_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, + ANY_EVENT + }; +} + +using stateMappingHandler = std::function; +class SingleVerSyncStateMachine final : public SyncStateMachine { +public: + + SingleVerSyncStateMachine(); + ~SingleVerSyncStateMachine() override; + + // Init the SingleVerSyncStateMachine + int Initialize(ISyncTaskContext *context, IKvDBSyncInterface *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); + + void SetSlidingWindowSenderErr(bool isErr); + + bool IsNeedErrCodeHandle(uint32_t sessionId) const; + + void PushPullDataRequestEvokeErrHandle(SingleVerSyncTaskContext *context); + +protected: + // Step the SingleVerSyncStateMachine + void SyncStep() override; + + // SyncOperation is timeout, step to timeout state + void StepToTimeout() 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) override; + +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(); + + // Do data packet sync for initiactive sync operation, if need pull, there need send a pull request. + Event DoInitiactiveDataSync(); + + // Do data packet sync for response pull request. + Event DoPassiveDataSync(); + + // Push remainder data to remote + Event DoInitiactivePushRemainderData(); + + // Push remainder data to remote for response pull request + Event DoPassivePushRemainderData(); + + // Waiting for pull data revice finish, if coming a pull request, should goto START_PASSIVE_DATA_SYNC state + Event DoWaitForDataRecv() const; + + // Sync task finished, 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(); + + int TimeMarkSyncRecv(const Message *inMsg); + + int AbilitySyncRecv(const Message *inMsg); + + int DataPktRecv(Message *inMsg); + + void NeedAbilitySyncHandle(); + + int HandleDataAckRecv(const Message *inMsg); + + int PreHandleAckRecv(const Message *inMsg); + + void HandleDataAckRecvWithSlidingWindow(int errCode, const Message *inMsg); + + void Clear(); + + bool IsPacketValid(const Message *inMsg) const; + + void PreStartPullResponse(); + + bool IsRightDataResponsePkt(const Message *inMsg) const; + + bool CheckIsStartPullResponse() const; + + int MessageCallbackPre(const Message *inMsg); + + void AddPullResponseTarget(WaterMark pullEndWatermark, uint32_t sessionId); + + Event TransformErrCodeToEvent(int errCode); + + bool IsNeedResetWatchdog(const Message *inMsg) const; + + Event TransforTimeOutErrCodeToEvent(); + + 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_; + std::unique_ptr dataSyncWithSlidingWindow_; + uint64_t currentRemoteVersionId_; + std::map stateMapping_; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_STATE_MACHINE_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.cpp new file mode 100755 index 000000000..40c328d4a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.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. + */ + +#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) +{ +} + +SingleVerSyncTarget::~SingleVerSyncTarget() +{ +} + +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_; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.h new file mode 100644 index 000000000..6d1fbde39 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_target.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sync_target.h" + +namespace DistributedDB { +class SingleVerSyncTarget final : public SyncTarget { +public: + SingleVerSyncTarget(); + ~SingleVerSyncTarget() override; + + // Set the end water mark of this task + void SetEndWaterMark(WaterMark waterMark); + + // Get the end water mark of this task + WaterMark GetEndWaterMark() const; + + void SetResponseSessionId(uint32_t responseSessionId); + + uint32_t GetResponseSessionId() const; + +private: + WaterMark endWaterMark_; + uint32_t responseSessionId_ = 0; +}; +} // namespace DistributedDB + +#endif // SINGLE_VER_SYNC_TARGET_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.cpp new file mode 100755 index 000000000..97c855411 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "log_print.h" +#include "isyncer.h" +#include "single_ver_sync_state_machine.h" +#include "single_ver_sync_target.h" + +namespace DistributedDB { +SingleVerSyncTaskContext::SingleVerSyncTaskContext() + : SyncTaskContext(), + token_(nullptr), + endMark_(0), + needClearRemoteStaleData_(false) +{} + +SingleVerSyncTaskContext::~SingleVerSyncTaskContext() +{ + token_ = nullptr; +} + +int SingleVerSyncTaskContext::Initialize(const std::string &deviceId, + IKvDBSyncInterface *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; + 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_); + auto iter = std::find_if(requestTargetQueue_.begin(), requestTargetQueue_.end(), [](const ISyncTarget *target) { + if (target == nullptr) { + return false; + } + return target->IsAutoSync(); + }); + if (iter != requestTargetQueue_.end()) { + static_cast(*iter)->SetEndWaterMark(timeHelper_->GetTime()); + operation->SetStatus(deviceId_, SyncOperation::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 = %d", 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 SingleVerSyncTarget *targetTmp = static_cast(target); + SyncTaskContext::CopyTargetData(target); + mode_ = targetTmp->GetMode(); + endMark_ = targetTmp->GetEndWaterMark(); + if (mode_ == SyncOperation::RESPONSE_PULL) { + responseSessionId_ = targetTmp->GetResponseSessionId(); + } +} + +void SingleVerSyncTaskContext::Clear() +{ + retryTime_ = 0; + ClearSyncOperation(); + SyncTaskContext::Clear(); + SetMode(SyncOperation::INVALID); + syncId_ = 0; + isAutoSync_ = false; + SetOperationStatus(SyncOperation::WAITING); + SetEndMark(0); + SetResponseSessionId(0); +} + +void SingleVerSyncTaskContext::Abort(int status) +{ + { + std::lock_guard lock(operationLock_); + if (syncOperation_ != nullptr) { + syncOperation_->SetStatus(deviceId_, status); + if ((status >= SyncOperation::FINISHED_ALL)) { + UnlockObj(); + if (syncOperation_->CheckIsAllFinished()) { + syncOperation_->Finished(); + } + LockObj(); + } + } + } + StopFeedDogForSync(SyncDirectionFlag::SEND); + StopFeedDogForSync(SyncDirectionFlag::RECEIVE); + Clear(); +} + +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); +} + +void SingleVerSyncTaskContext::SetSequenceStartAndEndTimeStamp(TimeStamp start, TimeStamp end) +{ + sequenceStartTimeStamp_ = start; + sequenceEndTimeStamp_ = end; +} + +TimeStamp SingleVerSyncTaskContext::GetSequenceStartTimeStamp() const +{ + return sequenceStartTimeStamp_; +} + +TimeStamp SingleVerSyncTaskContext::GetSequenceEndTimeStamp() const +{ + return sequenceEndTimeStamp_; +} + +void SingleVerSyncTaskContext::SetSessionEndTimeStamp(TimeStamp end) +{ + sessionEndTimeStamp_ = end; +} + +TimeStamp SingleVerSyncTaskContext::GetSessionEndTimeStamp() const +{ + return sessionEndTimeStamp_; +} + +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::SetSlidingWindowSenderErr(bool isErr) +{ + static_cast(stateMachine_)->SetSlidingWindowSenderErr(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::SetSyncStrategy(SyncStrategy strategy) +{ + syncStrategy_.permitSync = strategy.permitSync; + syncStrategy_.convertOnSend = strategy.convertOnSend; + syncStrategy_.convertOnReceive = strategy.convertOnReceive; + syncStrategy_.checkOnReceive = strategy.checkOnReceive; +} + +SyncStrategy SingleVerSyncTaskContext::GetSyncStrategy() const +{ + return syncStrategy_; +} + +void SingleVerSyncTaskContext::SetIsSchemaSync(bool isSchemaSync) +{ + isSchemaSync_ = isSchemaSync; +} + +bool SingleVerSyncTaskContext::GetIsSchemaSync() const +{ + return isSchemaSync_; +} + +bool SingleVerSyncTaskContext::IsSkipTimeoutError(int errCode) const +{ + if (errCode == -E_TIMEOUT && IsAutoSync() && (GetRetryTime() < ISyncStateMachine::RETRY_TIME)) { + LOGE("[SingleVerSyncTaskContext] send message timeout error occurred"); + return true; + } else { + return false; + } +} +DEFINE_OBJECT_TAG_FACILITIES(SingleVerSyncTaskContext) +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.h new file mode 100755 index 000000000..5f2f60137 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_sync_task_context.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sync_target.h" +#include "sync_task_context.h" +#include "time_helper.h" +#include "single_ver_sync_target.h" + +namespace DistributedDB { +class SingleVerSyncTaskContext final : public SyncTaskContext { +public: + + SingleVerSyncTaskContext(); + + DISABLE_COPY_ASSIGN_MOVE(SingleVerSyncTaskContext); + + // Init SingleVerSyncTaskContext + int Initialize(const std::string &deviceId, IKvDBSyncInterface *syncInterface, std::shared_ptr &metadata, + ICommunicator *communicator) override; + + // Add a sync task target with the operation to the queue + int AddSyncOperation(SyncOperation *operation) 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; + + // 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); + + // if sended by sliding window, get the start timeStamp of data in a sequence + TimeStamp GetSequenceStartTimeStamp() const; + + // if sended by sliding window, get the end timeStamp of data in a sequence + TimeStamp GetSequenceEndTimeStamp() const; + + int HandleDataRequestRecv(const Message *msg); + + // if sended by sliding window, set the start and and timeStamp of data in a sequence + void SetSequenceStartAndEndTimeStamp(TimeStamp start, TimeStamp end); + + // if sended by sliding window, set the last data timeStamp in a sync session + void SetSessionEndTimeStamp(TimeStamp end); + + // if sended by sliding window, get the last data timeStamp in a sync session + TimeStamp GetSessionEndTimeStamp() const; + + // is receive warterMark err + bool IsReceiveWaterMarkErr() const; + + // set receive warterMark err + void SetReceiveWaterMarkErr(bool isErr); + + // set sliding window sender err + void SetSlidingWindowSenderErr(bool isErr); + + void SetRemoteSeccurityOption(SecurityOption secOption); + + SecurityOption GetRemoteSeccurityOption() const; + + void SetReceivcPermitCheck(bool isChecked); + + bool GetReceivcPermitCheck() const; + + void SetSendPermitCheck(bool isChecked); + + bool GetSendPermitCheck() const; + + void SetSyncStrategy(SyncStrategy strategy); + + SyncStrategy GetSyncStrategy() const; + + void SetIsSchemaSync(bool isChecked); + + bool GetIsSchemaSync() const; + + bool IsSkipTimeoutError(int errCode) const; +protected: + ~SingleVerSyncTaskContext() override; + void CopyTargetData(const ISyncTarget *target) override; + +private: + 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; + SyncStrategy syncStrategy_; + bool isSchemaSync_ = false; + + // in a sync session, if sended by sliding window, the min timeStamp of data in a sequence + TimeStamp sequenceStartTimeStamp_ = 0; + // in a sync session, if sended by sliding window, the max timeStamp of data in a sequence + TimeStamp sequenceEndTimeStamp_ = 0; + // in a sync session, the last data timeStamp + TimeStamp sessionEndTimeStamp_ = 0; + // is receive waterMark err, peerWaterMark bigger than remote localWaterMark + bool isReceiveWaterMarkErr_ = false; +}; +} // namespace DistributedDB + +#endif // SYNC_TASK_CONTEXT_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.cpp new file mode 100755 index 000000000..4b87eb81f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "log_print.h" +#include "sqlite_single_ver_natural_store.h" +#include "single_ver_sync_engine.h" + +namespace DistributedDB { +SingleVerSyncer::SingleVerSyncer() + : autoSyncEnable_(false) +{ +} + +SingleVerSyncer::~SingleVerSyncer() +{ +} + +void SingleVerSyncer::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 syncId = Sync(devices, SyncOperation::AUTO_PUSH, nullptr, nullptr, false); + if (syncId < MIN_VALID_SYNC_ID) { + LOGE("[Syncer] sync start by EnableAutoSync failed err %d", syncId); + } +} + +int SingleVerSyncer::EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) +{ + if (metadata_ == nullptr) { + return -E_NOT_INIT; + } + return metadata_->EraseDeviceWaterMark(deviceId, isNeedHash); +} + +// Local data changed callback +void SingleVerSyncer::LocalDataChanged(int notifyEvent) +{ + if (!initialized_) { + LOGE("[Syncer] Syncer has not Init"); + return; + } + + if (!autoSyncEnable_) { + LOGD("[Syncer] autoSync no enable"); + return; + } + + if (notifyEvent != SQLITE_GENERAL_FINISH_MIGRATE_EVENT && + notifyEvent != SQLITE_GENERAL_NS_PUT_EVENT) { + LOGD("[Syncer] ignore event:%d", notifyEvent); + return; + } + + std::vector devices; + GetOnlineDevices(devices); + if (devices.empty()) { + LOGI("[Syncer] LocalDataChanged no online devices, Label=%s", label_.c_str()); + return; + } + + int syncId = Sync(devices, SyncOperation::AUTO_PUSH, nullptr, nullptr, false); + if (syncId < MIN_VALID_SYNC_ID) { + LOGE("[Syncer] sync start by RemoteDataChanged failed err %d", syncId); + } + return; +} + +// Remote data changed callback +void SingleVerSyncer::RemoteDataChanged(const std::string &device) +{ + LOGI("[SingleVerSyncer] device online dev %s{private}", device.c_str()); + if (autoSyncEnable_) { + RefObject::IncObjRef(syncEngine_); + int retCode = RuntimeContext::GetInstance()->ScheduleTask([this, device] { + std::vector devices; + devices.push_back(device); + int syncId = Sync(devices, SyncOperation::AUTO_PUSH, nullptr, nullptr, false); + if (syncId < MIN_VALID_SYNC_ID) { + LOGE("[SingleVerSyncer] sync start by RemoteDataChanged failed err %d", syncId); + } + RefObject::DecObjRef(syncEngine_); + }); + if (retCode != E_OK) { + LOGE("[AutoLaunch] RemoteDataChanged triggler sync retCode:%d", retCode); + RefObject::DecObjRef(syncEngine_); + } + } +} + +ISyncEngine *SingleVerSyncer::CreateSyncEngine() +{ + return new (std::nothrow) SingleVerSyncEngine(); +} + +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; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.h b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.h new file mode 100755 index 000000000..1a2176cd8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/single_ver_syncer.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 SYNCER_H +#define SYNCER_H + +#include "generic_syncer.h" + +namespace DistributedDB { +class SingleVerSyncer final : public GenericSyncer { +public: + SingleVerSyncer(); + ~SingleVerSyncer() override; + + // Enable auto sync function + void EnableAutoSync(bool enable) override; + + // delete specified device's watermark + int EraseDeviceWaterMark(const std::string &deviceId, bool isNeedHash) override; + + // Local data changed callback + void LocalDataChanged(int notifyEvent) override; + + // Remote data changed callback + void RemoteDataChanged(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; + +private: + bool autoSyncEnable_; +}; +} // namespace DistributedDB + +#endif // SYNCER_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.cpp new file mode 100755 index 000000000..35185ff11 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "sliding_window_receiver.h" +#include "sync_task_context.h" + +namespace DistributedDB { +SlidingWindowReceiver::~SlidingWindowReceiver() +{ + Clear(); + dataSync_ = nullptr; + context_ = nullptr; +} + +void SlidingWindowReceiver::Clear() +{ + StopTimer(); + std::lock_guard lock(lock_); + ClearMap(); + isWaterMarkErrHappened_ = false; +} + +int SlidingWindowReceiver::Initialize(SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + if (context == nullptr || dataSync == nullptr) { + LOGE("[slwr] Initialize invalid args"); + return -E_INVALID_ARGS; + } + context_ = context; + dataSync_ = dataSync; + return E_OK; +} + +int SlidingWindowReceiver::Receive(Message *inMsg) +{ + LOGD("[slwr] receive msg"); + if (inMsg == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = PutMsg(inMsg); + if (errCode == E_OK) { + StopTimer(); + StartTimer(); + DealMsg(); + return -E_NOT_NEED_DELETE_MSG; + } + return errCode; +} + +int SlidingWindowReceiver::PutMsg(Message *inMsg) +{ + const DataRequestPacket *packet = inMsg->GetObject(); + if (packet == nullptr) { + return -E_INVALID_ARGS; + } + uint32_t sessionId = inMsg->GetSessionId(); + uint32_t sequenceId = inMsg->GetSequenceId(); + bool isLastSequence = packet->IsLastSequence(); + uint64_t packetId = packet->GetPacketId(); // above 102 version data request reserve[0] store packetId value + std::unique_lock lock(lock_); + if (workingId_ != 0 && sessionId_ != 0) { + LOGI("[PutMsg] task is running, wait for workdingId=%u end,seId=%u", workingId_, sequenceId); + workingTaskcv_.wait(lock); + } + if (sessionId_ != sessionId) { + ResetInfo(); + sessionId_ = sessionId; + messageMap_[sequenceId] = inMsg; + SetEndField(isLastSequence, sequenceId); + return E_OK; + } + // maybe remote has not receive ack, we resend ack. + if (sequenceId <= hasFinishedMaxId_) { + LOGI("[slwr] seId=%u,FinishedMId_=%u,label=%s", sequenceId, hasFinishedMaxId_, dataSync_->GetLabel().c_str()); + lock.unlock(); + dataSync_->SendAck(context_, sessionId_, sequenceId, packetId); + return -E_SLIDING_WINDOW_RECEIVER_INVALID_MSG; + } + int errCode = ErrHandle(sequenceId); + if (errCode != E_OK) { + return errCode; + } + if (messageMap_.count(sequenceId) > 0) { + LOGI("[slwr] PutMsg sequenceId already in map"); + delete messageMap_[sequenceId]; + messageMap_[sequenceId] = nullptr; + } + messageMap_[sequenceId] = inMsg; + SetEndField(isLastSequence, sequenceId); + return E_OK; +} + +void SlidingWindowReceiver::DealMsg() +{ + while (true) { + Message *msg = nullptr; + { + std::lock_guard lock(lock_); + if (workingId_ != 0 || messageMap_.count(hasFinishedMaxId_ + 1) == 0) { + LOGI("[slwr] DealMsg do nothing workingId_=%u,hasFinishedMaxId_=%u,label=%s,deviceId=%s{private}", + workingId_, hasFinishedMaxId_, dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + return; + } + workingId_ = hasFinishedMaxId_ + 1; + msg = messageMap_[workingId_]; + messageMap_.erase(workingId_); + LOGI("[slwr] DealMsg workingId_=%u,label=%s,deviceId=%s{private}", workingId_, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + } + int errCode = context_->HandleDataRequestRecv(msg); + delete msg; + msg = nullptr; + { + std::lock_guard lock(lock_); + workingId_ = 0; + bool isWaterMarkErr = context_->IsReceiveWaterMarkErr(); + if (isWaterMarkErr || errCode == -E_NEED_ABILITY_SYNC) { + ClearMap(); + hasFinishedMaxId_ = 0; + endId_ = 0; + isWaterMarkErrHappened_ = true; + } else { + hasFinishedMaxId_++; + LOGI("[slwr] DealMsg ok hasFinishedMaxId_=%u,label=%s,deviceId=%s{private}", hasFinishedMaxId_, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + } + context_->SetReceiveWaterMarkErr(false); + workingTaskcv_.notify_all(); + } + } +} + +int SlidingWindowReceiver::TimeOut(TimerId timerId) +{ + { + std::lock_guard lock(lock_); + LOGI("[slwr] TimeOut,timerId[%llu], timerId_[%llu]", timerId, timerId_); + if (timerId == timerId_) { + ClearMap(); + } + timerId_ = 0; + } + RuntimeContext::GetInstance()->RemoveTimer(timerId); + return E_OK; +} + +void SlidingWindowReceiver::StartTimer() +{ + LOGD("[slwr] StartTimer"); + std::lock_guard lock(lock_); + TimerId timerId = 0; + RefObject::IncObjRef(context_); + TimerAction timeOutCallback = std::bind(&SlidingWindowReceiver::TimeOut, this, std::placeholders::_1); + int errCode = RuntimeContext::GetInstance()->SetTimer(IDLE_TIME_OUT, timeOutCallback, + [this]() { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this]() { + RefObject::DecObjRef(context_); + }); + if (errCode != E_OK) { + LOGE("[slwr] timer finalizer ScheduleTask, errCode=%d", errCode); + } + }, timerId); + if (errCode != E_OK) { + RefObject::DecObjRef(context_); + LOGE("[slwr] timer ScheduleTask, errCode=%d", errCode); + return; + } + timerId_ = timerId; +} + +void SlidingWindowReceiver::StopTimer() +{ + TimerId timerId; + { + std::lock_guard lock(lock_); + LOGD("[slwr] StopTimer,remove Timer id[%llu]", timerId_); + timerId = timerId_; + if (timerId_ == 0) { + return; + } + timerId_ = 0; + } + RuntimeContext::GetInstance()->RemoveTimer(timerId); +} + +void SlidingWindowReceiver::ClearMap() +{ + LOGD("[slwr] ClearMap"); + for (auto &iter : messageMap_) { + delete iter.second; + iter.second = nullptr; + } + messageMap_.clear(); +} + +void SlidingWindowReceiver::ResetInfo() +{ + ClearMap(); + hasFinishedMaxId_ = 0; + workingId_ = 0; + endId_ = 0; +} + +int SlidingWindowReceiver::ErrHandle(uint32_t sequenceId) +{ + if (sequenceId == workingId_ || (endId_ != 0 && sequenceId > endId_)) { + LOGI("[slwr] PutMsg sequenceId:%u, endId_:%u, workingId_:%u!", sequenceId, endId_, workingId_); + return -E_SLIDING_WINDOW_RECEIVER_INVALID_MSG; + } + // if waterMark err when DealMsg(), the waterMark of msg in messageMap_ is also err, so we clear messageMap_ + // but before the sender receive the waterMark err ack, the receiver may receive waterMark err msg, so before + // receiver receive the sequenceId 1 msg, it will drop msg. + // note, there is a low probability risk, if sender receive the waterMark err ack, it correct the warterMark then + // resend data, if the msg of bigger sequenceId arrive earlier than sequenceId 1, it will be drop, the sender + // will sync timeout. + if (isWaterMarkErrHappened_) { + if (sequenceId == 1) { + isWaterMarkErrHappened_ = false; + } else { + return -E_SLIDING_WINDOW_RECEIVER_INVALID_MSG; // drop invalid packet + } + } + return E_OK; +} + +void SlidingWindowReceiver::SetEndField(bool isLastSequence, uint32_t sequenceId) +{ + if (isLastSequence) { + endId_ = sequenceId; + } +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.h new file mode 100755 index 000000000..af882c148 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_receiver.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 SLIDING_WINDOW_RECEIVER_H +#define SLIDING_WINDOW_RECEIVER_H + +#include + +#include "message.h" +#include "single_ver_sync_task_context.h" +#include "single_ver_data_sync.h" + +namespace DistributedDB { +class SlidingWindowReceiver { +public: + SlidingWindowReceiver() = default; + ~SlidingWindowReceiver(); + DISABLE_COPY_ASSIGN_MOVE(SlidingWindowReceiver); + + int Initialize(SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + int Receive(Message *inMsg); + void Clear(); +private: + int PutMsg(Message *inMsg); + void DealMsg(); + int TimeOut(TimerId timerId); + void StartTimer(); + void StopTimer(); + void ClearMap(); + void ResetInfo(); + int ErrHandle(uint32_t sequenceId); + void SetEndField(bool isLastSequence, uint32_t sequenceId); + + std::mutex lock_; + std::condition_variable workingTaskcv_; + // 0 is default invalid. + uint32_t sessionId_ = 0; + // first:sequenceId second:Message*, message data not deal. + std::map messageMap_; + // 0 is has finished nothing; e.g. 3 is sequenceId 1 2 3 has finished, sequenceId 4 has not finished. + uint32_t hasFinishedMaxId_ = 0; + // 0 is idle + uint32_t workingId_ = 0; + // 0 is has not received the end pakcet now. + uint32_t endId_ = 0; + bool isWaterMarkErrHappened_ = false; + // timeout for idle wait packet + TimerId timerId_ = 0; + static constexpr int IDLE_TIME_OUT = 5 * 60 * 1000; // 5min + SingleVerSyncTaskContext *context_ = nullptr; + std::shared_ptr dataSync_; +}; +} // namespace DistributedDB +#endif // SLIDING_WINDOW_RECEIVER_H + diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.cpp new file mode 100755 index 000000000..5bbb5e383 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.cpp @@ -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. + */ + +#include "sliding_window_sender.h" + +namespace DistributedDB { +SlidingWindowSender::~SlidingWindowSender() +{ + Clear(); + context_ = nullptr; + dataSync_ = nullptr; +} + +int SlidingWindowSender::ParamCheck(int32_t mode, const SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + if (context == nullptr) { + LOGE("[slws] SendStart invalid context"); + return -E_INVALID_ARGS; + } + if (dataSync == nullptr) { + LOGE("[slws] SendStart invalid context"); + return -E_INVALID_ARGS; + } + return E_OK; +} + +void SlidingWindowSender::Init(int32_t mode, SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + isErr_ = false; + mode_ = mode; + context_->SetSessionEndTimeStamp(0); + context_->ReSetSequenceId(); + maxSequenceIdhasSent_ = 0; + windowSize_ = MAX_WINDOW_SIZE; + if (mode == SyncOperation::PUSH || mode == SyncOperation::PUSH_AND_PULL || mode == SyncOperation::PULL) { + sessionId_ = context->GetRequestSessionId(); + } else { + sessionId_ = context->GetResponseSessionId(); + } +} + +int SlidingWindowSender::Initialize(SingleVerSyncTaskContext *context, std::shared_ptr &dataSync) +{ + if (context == nullptr || dataSync == nullptr) { + LOGE("[slws] Initialize invalid args"); + return -E_INVALID_ARGS; + } + context_ = context; + dataSync_ = dataSync; + return E_OK; +} + +int SlidingWindowSender::SendStart(int32_t mode, SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync) +{ + int errCode = ParamCheck(mode, context, dataSync); + if (errCode != E_OK) { + return errCode; + } + LOGI("[slws] SendStart,mode=%d,label=%s,device=%s{private}", mode_, dataSync->GetLabel().c_str(), + context->GetDeviceId().c_str()); + errCode = dataSync->CheckPermitSendData(mode, context); + if (errCode != E_OK) { + return errCode; + } + std::lock_guard lock(lock_); + if (sessionId_ != 0) { // auto sync timeout resend + return ReSend(); + } + Init(mode, context, dataSync); + if (mode == SyncOperation::PUSH) { + errCode = dataSync->PushStart(context); + } else if (mode == SyncOperation::PUSH_AND_PULL) { + errCode = dataSync->PushPullStart(context); + } else if (mode == SyncOperation::PULL) { + errCode = dataSync->PullRequestStart(context); + } else { + errCode = dataSync->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. + UpdateInfo(); + return errCode; + } + if (errCode != E_OK) { + LOGE("[slws] SendStart errCode=%d", errCode); + return errCode; + } + errCode = UpdateInfo(); + if (mode == SyncOperation::PUSH_AND_PULL && context_->GetTaskErrCode() == -E_EKEYREVOKED) { + LOGE("wait for recv finished for push and pull mode"); + return -E_EKEYREVOKED; + } + if (errCode == -E_FINISHED) { + return E_OK; + } + return InnerSend(); +} + +int SlidingWindowSender::UpdateInfo() +{ + ReSendInfo reSendInfo; + reSendInfo.start = context_->GetSequenceStartTimeStamp(); + reSendInfo.end = context_->GetSequenceEndTimeStamp(); + reSendInfo.packetId = context_->GetPacketId(); + maxSequenceIdhasSent_++; + reSendMap_[maxSequenceIdhasSent_] = reSendInfo; + windowSize_--; + LOGI("[slws] mode=%d,start=%llu,end=%llu,seqId=%d,packetId=%llu,window_size=%d,label=%s,device=%s{private}", mode_, + reSendInfo.start, reSendInfo.end, maxSequenceIdhasSent_, reSendInfo.packetId, windowSize_, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + ContinueToken token; + context_->GetContinueToken(token); + if (token == nullptr) { + isAllDataHasSent_ = true; + LOGI("[slws] UpdateInfo all data has sent"); + return -E_FINISHED; + } + return E_OK; +} + +int SlidingWindowSender::InnerSend() +{ + while (true) { + if (windowSize_ <= 0 || isAllDataHasSent_) { + LOGI("[slws] InnerSend windowSize_=%d,isAllDataHasSent_=%d", windowSize_, isAllDataHasSent_); + return E_OK; + } + if (isErr_ || context_ == nullptr) { + LOGI("[slws] InnerSend isErr"); + return -E_SLIDING_WINDOW_SENDER_ERR; + } + int errCode; + context_->IncSequenceId(); + if (mode_ == SyncOperation::PUSH || mode_ == SyncOperation::PUSH_AND_PULL) { + errCode = dataSync_->PushStart(context_); + } else { + errCode = dataSync_->PullResponseStart(context_); + } + if (mode_ == SyncOperation::PUSH_AND_PULL && errCode == -E_EKEYREVOKED) { + LOGE("errCode = %d, wait for recv finished for push and pull mode", errCode); + 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. + UpdateInfo(); + return errCode; + } + if (errCode != E_OK) { + isErr_ = true; + LOGE("[slws] InnerSend errCode=%d", errCode); + return errCode; + } + errCode = UpdateInfo(); + if (errCode == -E_FINISHED) { + return E_OK; + } + } +} + +int SlidingWindowSender::PreHandleAckRecv(const Message *message) +{ + if (message == nullptr) { + LOGE("[slws] AckRecv message nullptr"); + return -E_INVALID_ARGS; + } + if (message->GetMessageType() == TYPE_NOTIFY) { + return E_OK; + } + 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 + 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("[slws] packetId[%llu] is not match with original[%llu]", packetId, originalPacketId); + return -E_SLIDING_WINDOW_RECEIVER_INVALID_MSG; + } + } + return E_OK; +} + +int SlidingWindowSender::AckRecv(const Message *message) +{ + if (message == nullptr) { + LOGE("[slws] 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(); + LOGI("[slws] AckRecv sequecneId=%d,packetId=%llu,label=%s,dev=%s{private}", sequenceId, packetId, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + + std::lock_guard lock(lock_); + if (sessionId != sessionId_) { + LOGI("[slws] AckRecv sessionId is different"); + return E_OK; + } + if (reSendMap_.count(sequenceId) != 0) { + reSendMap_.erase(sequenceId); + windowSize_++; + LOGI("[slws] AckRecv window_size=%d", windowSize_); + } else { + LOGI("[slws] AckRecv sequenceId not in map"); + return E_OK; + } + if (!isAllDataHasSent_) { + return InnerSend(); + } else if (reSendMap_.size() == 0) { + context_->SetOperationStatus(SyncOperation::SEND_FINISHED); + LOGI("[slws] AckRecv all finished,label=%s,dev=%s{private}", dataSync_->GetLabel().c_str(), + context_->GetDeviceId().c_str()); + InnerClear(); + return -E_FINISHED; + } + return E_OK; +} + +int SlidingWindowSender::ReSend() const +{ + if (isErr_) { + LOGI("[slws] ReSend InnerSend isErr"); + return -E_INTERNAL_ERROR; + } + if (reSendMap_.empty()) { + LOGI("[slws] ReSend map empty"); + return -E_INTERNAL_ERROR; + } + uint32_t sequenceId = reSendMap_.begin()->first; + ReSendInfo reSendInfo = reSendMap_.begin()->second; + LOGI("[slws] ReSend mode=%d,start=%llu,end=%llu,seqId=%d,packetId=%llu,windowsize=%d,label=%s,deviceId=%s{private}", + mode_, reSendInfo.start, reSendInfo.end, sequenceId, reSendInfo.packetId, windowSize_, + dataSync_->GetLabel().c_str(), context_->GetDeviceId().c_str()); + DataSyncReSendInfo dataReSendInfo = {sessionId_, sequenceId, reSendInfo.start, reSendInfo.end, reSendInfo.packetId}; + return dataSync_->ReSend(context_, dataReSendInfo); +} + +void SlidingWindowSender::Clear() +{ + std::lock_guard lock(lock_); + LOGD("[slws] clear sender info"); + InnerClear(); +} + +void SlidingWindowSender::InnerClear() +{ + sessionId_ = 0; + reSendMap_.clear(); + windowSize_ = 0; + maxSequenceIdhasSent_ = 0; + isAllDataHasSent_ = false; + isErr_ = false; +} + +void SlidingWindowSender::SetErr(bool isErr) +{ + std::lock_guard lock(lock_); + isErr_ = isErr; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.h new file mode 100755 index 000000000..74727ac4e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sliding_window_sender.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 SLIDING_WINDOW_SENDER_H +#define SLIDING_WINDOW_SENDER_H +#include + +#include "message.h" +#include "single_ver_sync_task_context.h" +#include "single_ver_data_sync.h" + +namespace DistributedDB { +struct ReSendInfo { + TimeStamp start = 0; + TimeStamp end = 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; +}; + +class SlidingWindowSender { +public: + SlidingWindowSender() = default; + ~SlidingWindowSender(); + DISABLE_COPY_ASSIGN_MOVE(SlidingWindowSender); + + int Initialize(SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + // start one dataSync, mode can be push pushAndPull or pullResponse + int SendStart(int32_t mode, SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + int AckRecv(const Message *message); + int PreHandleAckRecv(const Message *message); + void Clear(); + void SetErr(bool isErr); +private: + int ParamCheck(int32_t mode, const SingleVerSyncTaskContext *context, + std::shared_ptr &dataSync); + void Init(int32_t mode, SingleVerSyncTaskContext *context, std::shared_ptr &dataSync); + int UpdateInfo(); + int InnerSend(); + void InnerClear(); + int ReSend() const; // when timeout, used for reSend data + + // initial max window size + static const int MAX_WINDOW_SIZE = 5; + std::mutex lock_; + // 0 is default invalid sessionId. + uint32_t sessionId_ = 0; + // the sequenceId as key + std::map reSendMap_; + // remaining sending window + int32_t windowSize_ = 0; + // in this session, the max sequenceId has been sent + uint32_t maxSequenceIdhasSent_ = 0; + // int this session, all data has been sent, but all ack has received or not + bool isAllDataHasSent_ = false; + SingleVerSyncTaskContext *context_ = nullptr; + std::shared_ptr dataSync_ = nullptr; + int mode_ = 0; // sync mode, e.g. push + bool isErr_ = true; +}; +} // namespace DistributedDB +#endif // SLIDING_WINDOW_SENDER_H + diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.cpp new file mode 100755 index 000000000..658fa94ba --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "isync_state_machine.h" +#include "db_errno.h" +#include "log_print.h" +#include "hash.h" +#include "runtime_context.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 +#include "single_ver_data_sync.h" +#include "ability_sync.h" +#include "device_manager.h" +#include "db_common.h" + +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) +{ +} + +SyncEngine::~SyncEngine() +{ + LOGD("[SyncEngine] ~SyncEngine!"); + if (syncInterface_ != nullptr) { + syncInterface_->DecRefCount(); + syncInterface_ = nullptr; + } + if (deviceManager_ != nullptr) { + delete deviceManager_; + deviceManager_ = nullptr; + } + if (timeChangedListener_ != nullptr) { + timeChangedListener_->Drop(true); + timeChangedListener_ = nullptr; + } + communicator_ = nullptr; + metadata_ = nullptr; + LOGD("[SyncEngine] ~SyncEngine ok!"); +} + +int SyncEngine::Initialize(IKvDBSyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged) +{ + if ((syncInterface == nullptr) || (metadata == nullptr)) { + return -E_INVALID_ARGS; + } + syncInterface_ = syncInterface; + int 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. + syncInterface_ = nullptr; + return errCode; + } + onRemoteDataChanged_ = onRemoteDataChanged; + errCode = InitDeviceManager(onRemoteDataChanged); + if (errCode != E_OK) { + // reset ptr if initialize device manager failed + syncInterface_ = nullptr; + return errCode; + } + + metadata_ = metadata; + 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 + orgOffset) <= maxItemTime) { + orgOffset = maxItemTime - currentSysTime + TimeHelper::MS_TO_100_NS; // 1ms + } + this->metadata_->SaveLocalTimeOffset(orgOffset); + }, errCode); + if (timeChangedListener_ == nullptr) { + LOGE("[SyncEngine] Init RegisterTimeChangedLister failed"); + return errCode; + } + LOGI("[SyncEngine] Engine init ok"); + return E_OK; +} + +int SyncEngine::Close() +{ + LOGI("[SyncEngine] SyncEngine close enter!"); + if (timeChangedListener_ != nullptr) { + timeChangedListener_->Drop(true); + timeChangedListener_ = nullptr; + } + if (communicator_ != nullptr) { + communicator_->RegOnMessageCallback(nullptr, nullptr); + communicator_->RegOnConnectCallback(nullptr, nullptr); + communicator_->RegOnSendableCallback(nullptr, nullptr); + } + // Clear SyncContexts + { + std::unique_lock lock(contextMapLock_); + for (auto &iter : syncTaskContextMap_) { + ISyncTaskContext *tempContext = iter.second; + iter.second = nullptr; + lock.unlock(); + RefObject::KillAndDecObjRef(tempContext); + tempContext = nullptr; + lock.lock(); + } + } + if (communicator_ != 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 errCode; + } + communicatorAggregator->ReleaseCommunicator(communicator_); + communicator_ = nullptr; + } + + std::lock_guard msgLock(queueLock_); + while (!msgQueue_.empty()) { + Message *inMsg = msgQueue_.front(); + msgQueue_.pop_front(); + if (inMsg != nullptr) { + queueCacheSize_ -= GetMsgSize(inMsg); + delete inMsg; + } + } + + LOGI("[SyncEngine] SyncEngine closed!"); + return E_OK; +} + +int SyncEngine::AddSyncOperation(SyncOperation *operation) +{ + if (operation == nullptr) { + return -E_INVALID_ARGS; + } + + std::vector devices = operation->GetDevices(); + for (const auto &deviceId : devices) { + int checkErrCode = RunPermissionCheck(deviceId, operation->GetMode()); + if (checkErrCode == E_OK) { + operation->SetStatus(deviceId, SyncOperation::WAITING); + int errCode = AddSyncOperForContext(deviceId, operation); + if (errCode != E_OK) { + operation->SetStatus(deviceId, SyncOperation::FAILED); + } + } else { + operation->SetStatus(deviceId, SyncOperation::PERMISSION_CHECK_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) +{ + deviceManager_ = new (std::nothrow) DeviceManager(); + if (deviceManager_ == nullptr) { + LOGE("[SyncEngine] deviceManager alloc failed!"); + return -E_OUT_OF_MEMORY; + } + + int errCode = deviceManager_->Initialize(communicator_, onRemoteDataChanged); + 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 IKvDBSyncInterface *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(); + 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::MessageReceiveCallback, 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; + } + 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::MessageReceiveCallbackTask(ISyncTaskContext *context, const ICommunicator *communicator, + Message *inMsg) +{ + std::string deviceId = context->GetDeviceId(); + if (inMsg == nullptr) { + LOGE("[SyncEngine] MessageReceiveCallback inMsg is null!"); + goto MSG_CALLBACK_OUT; + } + + // deal remote local data changed message + if (inMsg->GetMessageId() == LOCAL_DATA_CHANGED) { + if (onRemoteDataChanged_ && deviceManager_->IsDeviceOnline(deviceId)) { + onRemoteDataChanged_(deviceId); + } else { + LOGE("[SyncEngine] onRemoteDataChanged is null!"); + } + } else { // others message, it should be dealt by context + int errCode = context->ReceiveMessageCallback(inMsg); + if (errCode == -E_NOT_NEED_DELETE_MSG) { + goto MSG_CALLBACK_OUT_NOT_DEL; + } + } + +MSG_CALLBACK_OUT: + delete inMsg; + inMsg = nullptr; +MSG_CALLBACK_OUT_NOT_DEL: + (void)DealMsgUtilQueueEmpty(); + { + std::lock_guard lock(queueLock_); + execTaskCount_--; + } + RefObject::DecObjRef(communicator); + RefObject::DecObjRef(context); + communicator = nullptr; + context = nullptr; +} + +int SyncEngine::DealMsgUtilQueueEmpty() +{ + 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); + } + + // 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. + ISyncTaskContext *nextContext = GetConextForMsg(inMsg->GetTarget(), errCode); + if (errCode != E_OK) { + delete inMsg; + inMsg = nullptr; + return errCode; + } + errCode = ScheduleDealMsg(nextContext, inMsg); + if (errCode != E_OK) { + RefObject::DecObjRef(nextContext); + delete inMsg; + inMsg = nullptr; + } + 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) +{ + RefObject::IncObjRef(communicator_); + { + std::lock_guard incLock(queueLock_); + execTaskCount_++; + } + int errCode = RuntimeContext::GetInstance()->ScheduleTask(std::bind(&SyncEngine::MessageReceiveCallbackTask, + this, context, communicator_, inMsg)); + if (errCode != E_OK) { + LOGE("[SyncEngine] MessageReceiveCallbackTask Schedule failed err %d", errCode); + RefObject::DecObjRef(communicator_); + { + std::lock_guard decLock(queueLock_); + execTaskCount_--; + } + } + return errCode; +} + +void SyncEngine::MessageReceiveCallback(const std::string &targetDev, Message *inMsg) +{ + int errCode = MessageReceiveCallbackInner(targetDev, inMsg); + if (errCode != E_OK) { + delete inMsg; + inMsg = nullptr; + LOGE("[SyncEngine] MessageReceiveCallback failed!"); + } +} + +int SyncEngine::MessageReceiveCallbackInner(const std::string &targetDev, Message *inMsg) +{ + if (targetDev.empty() || inMsg == nullptr) { + LOGE("[SyncEngine][MessageReceiveCallback] from a invalid device or inMsg is null "); + return -E_INVALID_ARGS; + } + + int 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); + return E_OK; + } + } + + int errCode = E_OK; + ISyncTaskContext *nextContext = GetConextForMsg(targetDev, errCode); + if (errCode != E_OK) { + return errCode; + } + + LOGD("[SyncEngine] MessageReceiveCallback 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; + return; + } + } + inMsg->SetTarget(targetDev); + msgQueue_.push_back(inMsg); + queueCacheSize_ += msgSize; + LOGE("[SyncEngine] The quantity of executing threads is beyond maximum. msgQueueSize = %d", 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: + return SingleVerDataSync::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::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 avliad!"); + return nullptr; + } + errCode = context->Initialize(deviceId, syncInterface_, metadata_, communicator_); + if (errCode != E_OK) { + LOGE("[SyncEngine] context init failed err %d, dev %s{private}", errCode, deviceId.c_str()); + 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{private} finalized", deviceId.c_str()); + 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(); + context->UnlockObj(); + int errCode = context->StartStateMachine(); + context->LockObj(); + if (errCode != E_OK) { + LOGE("[SyncEngine] machine StartSync failed"); + context->SetOperationStatus(SyncOperation::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; +} + +int SyncEngine::GetLocalIdentity(std::string &outTarget) const +{ + if (communicator_ == nullptr) { + LOGE("[SyncEngine] communicator_ is nullptr, return!"); + return -E_NOT_INIT; + } + std::string deviceId; + int errCode = communicator_->GetLocalIdentity(deviceId); + if (errCode != E_OK) { + LOGE("[SyncEngine] communicator_ GetLocalIdentity fail errCode:%d", errCode); + return errCode; + } + outTarget = DBCommon::TransferHashString(deviceId); + return E_OK; +} + +int SyncEngine::RunPermissionCheck(const std::string &deviceId, int mode) const +{ + uint8_t flag = 0; + if (mode == SyncOperation::PUSH) { + flag = CHECK_FLAG_SEND; + } else if (mode == SyncOperation::PULL) { + flag = CHECK_FLAG_RECEIVE; + } else if (mode == SyncOperation::PUSH_AND_PULL) { + flag = CHECK_FLAG_SEND | CHECK_FLAG_RECEIVE; + } + + 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{private} Label=%s", + errCode, flag, deviceId.c_str(), label_.c_str()); + } + return errCode; +} + +std::string SyncEngine::GetLabel() const +{ + return label_; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.h new file mode 100755 index 000000000..859607f49 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_engine.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 SYNC_ENGINE_H +#define SYNC_ENGINE_H + +#include +#include +#include + +#include "isync_engine.h" +#include "isync_task_context.h" +#include "task_pool.h" +#include "device_manager.h" + +namespace DistributedDB { +constexpr uint16_t NEW_SEND_TASK = 1; + +class SyncEngine : public ISyncEngine { +public: + SyncEngine(); + virtual ~SyncEngine(); + + // Do some init things + int Initialize(IKvDBSyncInterface *syncInterface, std::shared_ptr &metadata, + const std::function &onRemoteDataChanged) 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); + + // Get local deviceId, is hashed + int GetLocalIdentity(std::string &outTarget) const override; + + std::string GetLabel() const override; + +protected: + // Create a context + virtual ISyncTaskContext *CreateSyncTaskContext() = 0; + + // Find SyncTaskContext from the map + ISyncTaskContext *FindSyncTaskContext(const std::string &deviceId); + + // Used to store all send sync task infos (such as pull sync response, and push sync request) + std::map syncTaskContextMap_; + std::mutex contextMapLock_; + +private: + + // Init DeviceManager set callback + int InitDeviceManager(const std::function &onRemoteDataChanged); + + ISyncTaskContext *GetSyncTaskContext(const std::string &deviceId, int &errCode); + + // Init Comunicator, register callbacks + int InitComunicator(const IKvDBSyncInterface *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 MessageReceiveCallbackTask(ISyncTaskContext *context, const ICommunicator *communicator, Message *inMsg); + + // wrapper of MessageReceiveCallbackTask + void MessageReceiveCallback(const std::string &targetDev, Message *inMsg); + + // Sync Request Callback + int MessageReceiveCallbackInner(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, int mode) const; + + IKvDBSyncInterface *syncInterface_; + ICommunicator *communicator_; + DeviceManager *deviceManager_; + std::function onRemoteDataChanged_; + std::shared_ptr metadata_; + std::deque msgQueue_; + NotificationChain::Listener *timeChangedListener_; + unsigned int execTaskCount_; + std::string label_; + + 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_; +}; +} // namespace DistributedDB + +#endif // SYNC_ENGINE_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_operation.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_operation.cpp new file mode 100755 index 000000000..bc26dda79 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_operation.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ +} + +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, WAITING)); + } + if (mode_ == AUTO_PUSH) { + mode_ = PUSH; + isAutoSync_ = true; + } else if (mode_ == AUTO_PULL) { + mode_ = PULL; + isAutoSync_ = 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 >= FINISHED_ALL) { + return; + } + iter->second = status; + return; + } +} + +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() +{ + { + AutoLock lockGuard(this); + if (IsKilled() || isFinished_) { + return; + } + isFinished_ = true; + } + 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_); + userCallback_(statuses_); + } + 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::CheckIsAllFinished() const +{ + AutoLock lockGuard(this); + for (const auto &iter : statuses_) { + if (iter.second < FINISHED_ALL) { + return false; + } + } + return true; +} + +void SyncOperation::Finalize() +{ + if ((syncId_ > 0) && onFinalize_) { + LOGD("[SyncOperation] Callback SyncOperation onFinalize."); + onFinalize_(); + } +} + +DEFINE_OBJECT_TAG_FACILITIES(SyncOperation) +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.cpp new file mode 100755 index 000000000..911f698d2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#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, IKvDBSyncInterface *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 >= RETRY_TIME || !syncContext_->IsAutoSync()) { + LOGI("[SyncStateMachine][Timeout] TimeoutCallback retryTime:%d", retryTime); + syncContext_->UnlockObj(); + StepToTimeout(); + syncContext_->LockObj(); + return E_OK; + } + retryTime++; + syncContext_->SetRetryTime(retryTime); + // when version is high than 102, the sequenceid will be managed by slide windows sender. + if (syncContext_->GetRemoteSoftwareVersion() < SOFTWARE_VERSION_RELEASE_3_0) { + syncContext_->IncSequenceId(); + } + syncContext_->SetRetryStatus(SyncTaskContext::NEED_RETRY); + int timeoutTime = syncContext_->GetTimeoutTime(); + // set the new timeout value with 2 raised to the power of retryTime. + timeoutTime = timeoutTime * static_cast(pow(2, 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) +{ + if (syncContext_->GetRemoteSoftwareVersion() == SOFTWARE_VERSION_EARLIEST) { + currentSyncProctolVersion_ = SINGLE_VER_SYNC_PROCTOL_V2; // ver 101 102 use same table + } else if (syncContext_->GetRemoteSoftwareVersion() > SOFTWARE_VERSION_EARLIEST) { + currentSyncProctolVersion_ = syncContext_->GetRemoteSoftwareVersion(); + } + + 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] ver:%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] ver:%d, Can't find event %u int currentSate %u ignore", + (*tableIter).version, event, currentState_); + return -E_NOT_FOUND; + } + + currentState_ = stateIter->second; + LOGD("[SyncStateMachine][SwitchState] ver:%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() +{ + if (syncContext_->IsTargetQueueEmpty()) { + syncContext_->SetTaskExecStatus(ISyncTaskContext::FINISHED); + syncContext_->Clear(); + LOGD("[SyncStateMachine] All sync task finished!"); + return -E_NO_SYNC_TASK; + } + syncContext_->MoveToNextTarget(); + int errCode = PrepareNextSyncTask(); + if (errCode != E_OK) { + LOGE("[SyncStateMachine] PrepareSync failed"); + syncContext_->SetOperationStatus(SyncOperation::FAILED); + } + return errCode; +} + +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) +{ + 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](TimerId timerId) { + { + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + } + std::lock_guard innerLock(saveDataNotifyLock_); + if (saveDataNotifyCount_ >= MAXT_SAVE_DATA_NOTIFY_COUNT) { + StopSaveDataNotifyNoLock(); + return E_OK; + } + SendSaveDataNotifyPacket(sessionId, sequenceId); + saveDataNotifyCount_++; + return E_OK; + }, + [this]() { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(syncContext_); }); + if (errCode != E_OK) { + LOGE("[SyncStateMachine] [SaveDataNotify] timer finalizer ScheduleTask, errCode %d", errCode); + } + }, + 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; + } + std::lock_guard lockGuard(feedDogLock_[flag]); + if (feedDogTimerId_[flag] > 0) { + feedDogCount_[flag] = 0; + LOGW("[SyncStateMachine][feedDog] timer has been started!, flag:%d", flag); + return false; + } + int cnt = time / SAVE_DATA_NOTIFY_INTERVAL; + LOGI("[SyncStateMachine][feedDog] start cnt:%d, flag:%d", cnt, flag); + + // Incref to make sure context still alive before timer stopped. + RefObject::IncObjRef(syncContext_); + int errCode = RuntimeContext::GetInstance()->SetTimer( + SAVE_DATA_NOTIFY_INTERVAL, + [this, flag, cnt](TimerId timerId) { + { + std::lock_guard lock(stateMachineLock_); + (void)ResetWatchDog(); + } + std::lock_guard innerLock(feedDogLock_[flag]); + if (feedDogCount_[flag] >= cnt) { + StopFeedDogForSyncNoLock(flag); + return E_OK; + } + feedDogCount_[flag]++; + return E_OK; + }, + [this]() { + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(syncContext_); }); + if (errCode != E_OK) { + LOGE("[SyncStateMachine] [feedDog] timer finalizer ScheduleTask, errCode %d", errCode); + } + }, + feedDogTimerId_[flag]); + if (errCode != E_OK) { + LOGW("[SyncStateMachine][feedDog] start timer failed err %d !", errCode); + return false; + } + return true; +} + +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 (feedDogTimerId_[flag] == 0) { + return; + } + LOGI("[SyncStateMachine][feedDog] stop flag:%d", flag); + RuntimeContext::GetInstance()->RemoveTimer(feedDogTimerId_[flag]); + feedDogTimerId_[flag] = 0; + feedDogCount_[flag] = 0; +} + +void SyncStateMachine::SetCurStateErrStatus() +{ +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.h new file mode 100755 index 000000000..8d6c2066a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_state_machine.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 SYNC_STATE_MACHINE_H +#define SYNC_STATE_MACHINE_H + +#include + +#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 +}; + +class SyncStateMachine : public ISyncStateMachine { +public: + SyncStateMachine(); + virtual ~SyncStateMachine(); + + // Init the SingleVerSyncStateMachine + int Initialize(ISyncTaskContext *context, IKvDBSyncInterface *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; + + // 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() = 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) = 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 + 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); + + // 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); + + DISABLE_COPY_ASSIGN_MOVE(SyncStateMachine); + + ISyncTaskContext *syncContext_; + IKvDBSyncInterface *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]; + TimerId feedDogTimerId_[SYNC_DIRECTION_NUM] = {0, 0}; + uint8_t feedDogCount_[SYNC_DIRECTION_NUM] = {0, 0}; +}; +} // namespace DistributedDB +#endif // SYNC_STATE_MACHINE_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.cpp new file mode 100755 index 000000000..d6d350add --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.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 "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(); +} +} // namespace DistributedDB + diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.h new file mode 100755 index 000000000..93c01f117 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_target.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 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) {}; + virtual ~SyncTarget(); + + // 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 Sync Status, it will increase the ref of operation + void SetSyncOperation(SyncOperation *operation) override; + + // Get a SyncOperation + void GetSyncOperation(SyncOperation *&operation) const override; + + // Is this target is a auto sync + bool IsAutoSync() const override; + +protected: + SyncOperation *operation_; + int taskType_; // sync task or response task; + int mode_; +}; +} // namespace DistributedDB + +#endif // SYNC_TARGET_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.cpp new file mode 100755 index 000000000..e2a7b940d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.cpp @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "db_errno.h" +#include "log_print.h" +#include "isync_state_machine.h" +#include "hash.h" +#include "time_helper.h" +#include "db_constant.h" + +namespace DistributedDB { +std::mutex SyncTaskContext::synTaskContextSetLock_; +std::set SyncTaskContext::synTaskContextSet_; + +SyncTaskContext::SyncTaskContext() + : syncOperation_(nullptr), + syncId_(0), + mode_(0), + isAutoSync_(false), + status_(0), + taskExecStatus_(0), + syncInterface_(nullptr), + communicator_(nullptr), + stateMachine_(nullptr), + requestSessionId_(0), + timeHelper_(nullptr), + remoteSoftwareVersion_(0), + remoteSoftwareVersionId_(0), + isCommNormal_(true), + taskErrCode_(E_OK) +{ +} + +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::SEND_FINISHED && operationStatus == SyncOperation::RECV_FINISHED) { + if (GetTaskErrCode() == -E_EKEYREVOKED) { + finalStatus = SyncOperation::EKEYREVOKED_FAILURE; + } else { + finalStatus = SyncOperation::FINISHED_ALL; + } + } else if (status == SyncOperation::RECV_FINISHED && operationStatus == SyncOperation::SEND_FINISHED) { + if (GetTaskErrCode() == -E_EKEYREVOKED) { + finalStatus = SyncOperation::EKEYREVOKED_FAILURE; + } else { + finalStatus = SyncOperation::FINISHED_ALL; + } + } + syncOperation_->SetStatus(deviceId_, finalStatus); + if (syncOperation_->CheckIsAllFinished()) { + syncOperation_->Finished(); + } +} + +void SyncTaskContext::Clear() +{ + StopTimer(); + retryTime_ = 0; + sequenceId_ = 1; + syncId_ = 0; + isAutoSync_ = false; + requestSessionId_ = 0; + isNeedRetry_ = NO_NEED_RETRY; + mode_ = SyncOperation::INVALID; + status_ = SyncOperation::WAITING; + taskErrCode_ = E_OK; + packetId_ = 0; +} + +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::FINISHED_ALL; + } + return syncOperation_->GetStatus(deviceId_); +} +void SyncTaskContext::SetMode(int mode) +{ + mode_ = mode; +} + +int SyncTaskContext::GetMode() const +{ + return mode_; +} +void SyncTaskContext::MoveToNextTarget() +{ + ClearSyncOperation(); + 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); + 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() +{ + if (!isAutoSync_) { + timeout_ = DBConstant::MANUAL_SYNC_TIMEOUT; + } else { + timeout_ = DBConstant::AUTO_SYNC_TIMEOUT; + } + 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 errCode = RuntimeContext::GetInstance()->ScheduleTask([this](){ RefObject::DecObjRef(this); }); + if (errCode != E_OK) { + LOGE("[SyncTaskContext] timer finalizer ScheduleTask, errCode %d", errCode); + } + }, 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) +{ + 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, 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) { + isCommNormal_ = true; + 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) +{ + mode_ = target->GetMode(); + status_ = SyncOperation::SYNCING; + isNeedRetry_ = SyncTaskContext::NO_NEED_RETRY; + taskErrCode_ = E_OK; + packetId_ = 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(); + requestSessionId_ = Hash::Hash32Func(deviceId_ + std::to_string(syncId_) + + std::to_string(TimeHelper::GetSysCurrentTime())); + LOGI("[SyncTaskContext][copyTarget] mode_ = %d, syncId_ = %d, isAutoSync_ = %d, dev = %s{private}", + mode_, syncId_, isAutoSync_, deviceId_.c_str()); + } else { + isAutoSync_ = false; + LOGI("[SyncTaskContext][copyTarget] for response data dev %s{private} ", deviceId_.c_str()); + } +} + +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 maybe 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) { + RefObject::DecObjRef(syncOperation_); + syncOperation_ = nullptr; + } +} + +void SyncTaskContext::CancelCurrentSyncRetryIfNeed(int newTargetMode) +{ + AutoLock(this); + if (!isAutoSync_) { + return; + } + if (newTargetMode == mode_ || newTargetMode == SyncOperation::PUSH_AND_PULL) { + SetRetryTime(ISyncStateMachine::RETRY_TIME); + ModifyTimer(DBConstant::AUTO_SYNC_TIMEOUT); + } +} + +int SyncTaskContext::GetTaskErrCode() const +{ + return taskErrCode_; +} + +void SyncTaskContext::SetTaskErrCode(int errCode) +{ + taskErrCode_ = errCode; +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.h b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.h new file mode 100755 index 000000000..b293f4523 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/sync_task_context.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "isync_task_context.h" +#include "sync_target.h" +#include "semaphore.h" +#include "sync_operation.h" +#include "icommunicator.h" +#include "ikvdb_sync_interface.h" +#include "meta_data.h" +#include "runtime_context.h" +#include "time_helper.h" + +namespace DistributedDB { +enum SyncDirectionFlag { + SEND = 0, + RECEIVE = 1, +}; + +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; + +protected: + const static int KILL_WAIT_SECONDS = INT32_MAX; + + ~SyncTaskContext() override; + + virtual int TimeOut(TimerId id); + + virtual void CopyTargetData(const ISyncTarget *target); + + void CommErrHandlerFuncInner(int errCode, uint32_t sessionId); + + void KillWait(); + + void ClearSyncOperation(); + + void ClearSyncTarget(); + + void CancelCurrentSyncRetryIfNeed(int newTargetMode); + + 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_; + IKvDBSyncInterface *syncInterface_; + ICommunicator *communicator_; + ISyncStateMachine *stateMachine_; + TimeOffset timeOffset_ = 0; + int retryTime_ = 0; + int isNeedRetry_ = SyncTaskContext::NO_NEED_RETRY; + uint32_t requestSessionId_ = 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 + + // For gloable ISyncTaskContext Set, used by CommErrCallback. + static std::mutex synTaskContextSetLock_; + static std::set synTaskContextSet_; +}; +} // namespace DistributedDB + +#endif // SYNC_TASK_CONTEXT_H diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_factory.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_factory.cpp new file mode 100755 index 000000000..bfc3b2af2 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_factory.cpp @@ -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. + */ + +#include "syncer_factory.h" + +#include "single_ver_syncer.h" +#include "multi_ver_syncer.h" +#include "ikvdb_sync_interface.h" + +namespace DistributedDB { +std::shared_ptr SyncerFactory::GetSyncer(int type) +{ + if (type == IKvDBSyncInterface::SYNC_SVD) { + std::shared_ptr singleVerSyncer(std::make_shared()); + return singleVerSyncer; +#ifndef OMIT_MULTI_VER + } else if (type == IKvDBSyncInterface::SYNC_MVD) { + std::shared_ptr multiVerSyncer(std::make_shared()); + return multiVerSyncer; +#endif + } else { + return nullptr; + } +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_proxy.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_proxy.cpp new file mode 100755 index 000000000..365ae3a8f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/syncer_proxy.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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(IKvDBSyncInterface *syncInterface) +{ + 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); +} + +int SyncerProxy::Close() +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->Close(); +} + +int SyncerProxy::Sync(const std::vector &devices, int mode, + const std::function &)> &onComplete, + const std::function &onFinalize, bool wait = false) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->Sync(devices, mode, onComplete, onFinalize, wait); +} + +int SyncerProxy::RemoveSyncOperation(int syncId) +{ + if (syncer_ == nullptr) { + return -E_NOT_INIT; + } + return syncer_->RemoveSyncOperation(syncId); +} + +uint64_t SyncerProxy::GetTimeStamp() +{ + if (syncer_ == nullptr) { + return SyncerFactory::GetSyncer(IKvDBSyncInterface::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) +{ + if (syncer_ == nullptr) { + return SyncerFactory::GetSyncer(IKvDBSyncInterface::SYNC_SVD)->EraseDeviceWaterMark(deviceId, isNeedHash); + } + return syncer_->EraseDeviceWaterMark(deviceId, isNeedHash); +} + +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); +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/time_helper.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/time_helper.cpp new file mode 100755 index 000000000..fc97aea3d --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/time_helper.cpp @@ -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. + */ + +#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 IKvDBSyncInterface *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 = maxItemTime - currentSysTime + MS_TO_100_NS; // 1ms + SaveLocalTimeOffset(localTimeOffset); + } + metadata_->SetLastLocalTime(currentSysTime + 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); +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.cpp new file mode 100755 index 000000000..adf1348e8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + constexpr int TIME_SYNC_WAIT_TIME = 5; // 5s +} + +// 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 IKvDBSyncInterface *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) +{ + isOnline_ = true; + TimeSyncPacket packet; + TimeStamp startTime = timeHelper_->GetTime(); + packet.SetSourceTimeBegin(startTime); + // send timeSync request + LOGD("[TimeSync] startTime = %llu, 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->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) +{ + 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 = %llu, tEnd = %llu, sBegin = %llu, tBegin = %llu, offset = %lld", + 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 = %llu, tTimeEnd = %llu, sbegin = %llu, tbegin = %llu", + 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 = 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->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 = (timeSyncInfo.GetSourceTimeEnd() - timeSyncInfo.GetSourceTimeBegin()) - + (timeSyncInfo.GetTargetTimeEnd() - timeSyncInfo.GetTargetTimeBegin()); + TimeOffset offset1 = timeSyncInfo.GetTargetTimeBegin() - + timeSyncInfo.GetSourceTimeBegin() - (roundTrip / TRIP_DIV_HALF); + TimeOffset offset2 = timeSyncInfo.GetTargetTimeEnd() + (roundTrip / TRIP_DIV_HALF) - + timeSyncInfo.GetSourceTimeEnd(); + TimeOffset offset = (offset1 / TRIP_DIV_HALF) + (offset2 / TRIP_DIV_HALF); + LOGD("TimeSync::CalculateTimeOffset roundTrip= %lld, offset1 = %lld, offset2 = %lld, offset = %lld", + 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 = %d, inMsg type = %d", messageType, inMsg->GetMessageType()); + return false; + } + return true; +} + +int TimeSync::SendPacket(const DeviceID &deviceId, const Message *message, const CommErrHandler &handler) +{ + int errCode = communicateHandle_->SendMessage(deviceId, message, false, SEND_TIME_OUT, 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) +{ + if (!isSynced_) { + { + std::lock_guard lock(cvLock_); + isAckReceived_ = false; + } + CommErrHandler handler = std::bind(&TimeSync::CommErrHandlerFunc, std::placeholders::_1, this); + int errCode = SyncStart(handler); + LOGD("TimeSync::GetTimeOffset start, current = %llu, errCode:%d", TimeHelper::GetSysCurrentTime(), errCode); + std::unique_lock lock(cvLock_); + if (errCode != E_OK || !conditionVar_.wait_for(lock, std::chrono::seconds(TIME_SYNC_WAIT_TIME), + [this](){ return this->isAckReceived_ == true; })) { + LOGD("TimeSync::GetTimeOffset, retryTime_ = %d", retryTime_); + retryTime_++; + if (retryTime_ < MAX_RETRY_TIME) { + lock.unlock(); + return GetTimeOffset(outOffset); + } + 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::unique_lock 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/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.h b/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.h new file mode 100755 index 000000000..7730e5c34 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/time_sync.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 TIME_SYNC_H +#define TIME_SYNC_H + +#include "icommunicator.h" +#include "meta_data.h" +#include "sync_task_context.h" +#include "time_helper.h" +#include "ref_object.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 IKvDBSyncInterface *storage, const DeviceID &deviceId); + + int SyncStart(const CommErrHandler &handler = nullptr); // send timesync request + + int AckRecv(const Message *message); + + int RequestRecv(const Message *message); + + // Get timeoffset from metadata + int GetTimeOffset(TimeOffset &outOffset); + + 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/services/distributeddataservice/libs/distributeddb/syncer/src/value_slice_sync.cpp b/services/distributeddataservice/libs/distributeddb/syncer/src/value_slice_sync.cpp new file mode 100755 index 000000000..5659b6ed8 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/syncer/src/value_slice_sync.cpp @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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(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 = %lu, length = %lu", 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) +{ + int errCode = communicateHandle_->SendMessage(deviceId, inMsg, false, SEND_TIME_OUT); + 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/services/distributeddataservice/libs/distributeddb/test/BUILD.gn b/services/distributeddataservice/libs/distributeddb/test/BUILD.gn new file mode 100755 index 000000000..6e108c0f9 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/BUILD.gn @@ -0,0 +1,666 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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/src", + "../storage/include", + "../storage/src", + "../storage/src/multiver", + "../storage/src/operation", + "../storage/src/sqlite", + "../storage/src/upgrader", + "../common/include", + "../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", + ] +} + +############################################################################### +ohos_source_set("src_file") { + testonly = true + + sources = [ + "../common/src/auto_launch.cpp", + "../common/src/db_common.cpp", + "../common/src/db_constant.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/runtime_context.cpp", + "../common/src/runtime_context_impl.cpp", + "../common/src/schema_object.cpp", + "../common/src/schema_utils.cpp", + "../common/src/semaphore.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/value_object.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/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", + "../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/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/result_entries_window.cpp", + "../storage/src/single_ver_natural_store_commit_notify_data.cpp", + "../storage/src/sqlite/query_object.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_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_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_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_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/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/single_ver_data_sync.cpp", + "../syncer/src/single_ver_data_sync_with_sliding_window.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/sliding_window_receiver.cpp", + "../syncer/src/sliding_window_sender.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/virtual_multi_ver_sync_db_interface.cpp", + "unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp", + "unittest/common/syncer/vitural_communicator.cpp", + "unittest/common/syncer/vitural_communicator_aggregator.cpp", + "unittest/common/syncer/vitural_device.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//third_party/flatbuffers:flatbuffers_mini", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + ] + + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +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/flatbuffers:flatbuffers_mini", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + ] + configs += [ "//third_party/jsoncpp:jsoncpp_config" ] + deps += [ + "//third_party/jsoncpp:jsoncpp", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "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("DistributedDBInterfacesNBDelegateTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_nb_delegate_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", + ] +} + +ohos_unittest("DistributedDBStorageEncryptTest") { + module_out_path = module_output_path + include_dirs = [ + "../include", + "../common/include", + "//third_party/sqlite/include", + "//utils/native/base/include", + ] + + sources = [ + "../common/src/log_print.cpp", + "//third_party/sqlite/src/sqlite3.c", + "unittest/common/storage/distributeddb_storage_encrypt_test.cpp", + ] + + configs = [ ":module_private_config" ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + defines = [ + "NDEBUG=1", + "HAVE_USLEEP=1", + "SQLITE_HAVE_ISNAN", + "SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576", + "SQLITE_THREADSAFE=2", + "SQLITE_TEMP_STORE=3", + "SQLITE_POWERSAFE_OVERWRITE=1", + "SQLITE_DEFAULT_FILE_FORMAT=4", + "SQLITE_DEFAULT_AUTOVACUUM=1", + "SQLITE_ENABLE_MEMORY_MANAGEMENT=1", + "SQLITE_ENABLE_FTS3", + "SQLITE_ENABLE_FTS4", + "SQLITE_OMIT_COMPILEOPTION_DIAGS", + "SQLITE_OMIT_LOAD_EXTENSION", + "SQLITE_DEFAULT_FILE_PERMISSIONS=0600", + "SQLITE_SECURE_DELETE", + "SQLITE_ENABLE_BATCH_ATOMIC_WRITE", + "USE_PREAD64", + "fdatasync=fdatasync", + "HAVE_MALLOC_H=1", + "HAVE_MALLOC_USABLE_SIZE", + "SQLITE_DIRECT_OVERFLOW_READ", + "SQLITE_HAS_CODEC", + "SQLITE_CODEC_ATTACH_CHANGED", + ] + + deps = [ + "//third_party/openssl:libcrypto_static", + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] +} + +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("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" ] +} + +############################################################################### +group("unittest") { + testonly = true + deps = [ "//third_party/googletest:gmock" ] + + deps += [ + ":DistributedDBAbilitySyncTest", + ":DistributedDBAutoLaunchUnitTest", + ":DistributedDBCommunicatorDeepTest", + ":DistributedDBCommunicatorSendReceiveTest", + ":DistributedDBCommunicatorTest", + ":DistributedDBDeviceIdentifierTest", + ":DistributedDBEventLoopTimerTest", + ":DistributedDBFilePackageTest", + ":DistributedDBInterfacesAutoLaunchTest", + ":DistributedDBInterfacesDataOperationSyncDBTest", + ":DistributedDBInterfacesDataOperationTest", + ":DistributedDBInterfacesDatabaseCorruptTest", + ":DistributedDBInterfacesDatabaseTest", + ":DistributedDBInterfacesEncryptDatabaseTest", + ":DistributedDBInterfacesEncryptDelegateTest", + ":DistributedDBInterfacesImportAndExportTest", + ":DistributedDBInterfacesIndexUnitTest", + ":DistributedDBInterfacesNBDelegateLocalBatchTest", + ":DistributedDBInterfacesNBDelegateSchemaPutTest", + ":DistributedDBInterfacesNBDelegateTest", + ":DistributedDBInterfacesNBPublishTest", + ":DistributedDBInterfacesNBResultsetPerfTest", + ":DistributedDBInterfacesNBTransactionTest", + ":DistributedDBInterfacesNBUnpublishTest", + ":DistributedDBInterfacesQueryDBTest", + ":DistributedDBInterfacesRegisterSyncDBTest", + ":DistributedDBInterfacesSchemaDatabaseUpgradeTest", + ":DistributedDBInterfacesSpaceManagementTest", + ":DistributedDBInterfacesTransactionOptimizationTest", + ":DistributedDBInterfacesTransactionSyncDBTest", + ":DistributedDBInterfacesTransactionTest", + ":DistributedDBJsonPrecheckUnitTest", + ":DistributedDBMultiVerP2PSyncTest", + ":DistributedDBMultiVerVacuumTest", + ":DistributedDBNotificationChainTest", + ":DistributedDBParcelTest", + ":DistributedDBSchemaObjectTest", + ":DistributedDBSchemalTest", + ":DistributedDBSingleVerP2PSyncCheckTest", + ":DistributedDBSingleVerP2PSyncTest", + ":DistributedDBSingleVersionResultSetTest", + ":DistributedDBSqliteRegisterTest", + ":DistributedDBStorageCommitStorageTest", + ":DistributedDBStorageDataOperationTest", + ":DistributedDBStorageEncryptTest", + ":DistributedDBStorageIndexOptimizeTest", + ":DistributedDBStorageMemorySingleVerNaturalStoreTest", + ":DistributedDBStorageRegisterConflictTest", + ":DistributedDBStorageRegisterObserverTest", + ":DistributedDBStorageResultAndJsonOptimizeTest", + ":DistributedDBStorageSQLiteSingleVerNaturalStoreTest", + ":DistributedDBStorageSingleVerUpgradeTest", + ":DistributedDBStorageTransactionDataTest", + ":DistributedDBStorageTransactionRecordTest", + ":DistributedDBSyncerDeviceManagerTest", + ":DistributedDBTimeSyncTest", + ":RuntimeContextProcessSystemApiAdapterImplTest", + ] +} +############################################################################### diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp new file mode 100755 index 000000000..989c2a3e5 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_auto_launch_test.cpp @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_errno.h" +#include "log_print.h" +#include "db_common.h" +#include "kvdb_manager.h" +#include "distributeddb_tools_unit_test.h" +#include "vitural_communicator_aggregator.h" +#include "platform_specific.h" +#include "kv_store_nb_conflict_data.h" +#include "kvdb_pragma.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; +} + +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) +{ + 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); + 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); +} + +void DistributedDBAutoLaunchUnitTest::SetUp(void) +{ + 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); + GetProperty(g_propB, g_identifierB, STORE_ID_1); + GetProperty(g_propC, g_identifierC, STORE_ID_2); + GetProperty(g_propD, g_identifierD, STORE_ID_3); + GetProperty(g_propE, g_identifierE, STORE_ID_4); + GetProperty(g_propF, g_identifierF, STORE_ID_5); + GetProperty(g_propG, g_identifierG, STORE_ID_6); + GetProperty(g_propH, g_identifierH, STORE_ID_7); + GetProperty(g_propI, g_identifierI, STORE_ID_8); +} + +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:%lld", 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, nullptr, nullptr, 0, nullptr); + 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, nullptr, 0, nullptr); + EXPECT_TRUE(errCode != E_OK); + + /** + * @tc.steps: step3. right param C enable + * @tc.expected: step3. success. + */ + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step4. param A disable + * @tc.expected: step4. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step5. param B disable + * @tc.expected: step5. -E_NOT_FOUND. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + EXPECT_TRUE(errCode == -E_NOT_FOUND); + + /** + * @tc.steps: step6. param C disable + * @tc.expected: step6. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC); + 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, observer, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, observer, 0, nullptr); + EXPECT_TRUE(errCode == 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 + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + 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[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():%d", 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, observer, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, observer, 0, nullptr); + 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); + EXPECT_TRUE(errCode == E_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propD, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propE, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propF, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propG, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propH, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step2. right param I enable + * @tc.expected: step2. -E_MAX_LIMITS. + */ + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propI, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == -E_MAX_LIMITS); + + /** + * @tc.steps: step3. param A disable + * @tc.expected: step3. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step4. right param I enable + * @tc.expected: step4. E_OK. + */ + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propI, nullptr, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + + /** + * @tc.steps: step6. param B~I disable + * @tc.expected: step6. E_OK. + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierD); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierE); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierF); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierG); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierH); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierI); + 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():%d", 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, observer, 0, nullptr); + 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 + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + 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 thread([&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(); + }); + thread.detach(); + + for (int i = 0; i < TEST_ENABLE_CNT; i++) { + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, notifier, observer, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, observer, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + EXPECT_TRUE(errCode == E_OK); + LOGD("AutoLaunch006 disable i:%d", i); + } + std::unique_lock lock(cvLock); + cv.wait(lock, [&threadIsWorking]{return threadIsWorking == false;}); + + delete observer; + 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)); + /** + * @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); + 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)); + /** + * @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); + delete observer; +} + +/** + * @tc.name: AutoLaunch009 + * @tc.desc: enhancement callback return bad param + * @tc.type: FUNC + * @tc.require: AR000EPARJ + * @tc.author: wangchuanqing + */ +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); + /** + * @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); + 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)); + + RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(nullptr); + /** + * @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); + 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propA, TestAutoLaunchNotifier, nullptr, + CONFLICT_FOREIGN_KEY_ONLY, ConflictNotifierCallback); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, nullptr, nullptr, 0, nullptr); + 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 + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierA); + 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. + */ + int errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propB, notifier, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->EnableKvStoreAutoLaunch(g_propC, notifier, nullptr, 0, nullptr); + EXPECT_TRUE(errCode == 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)); + + /** + * @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); + delete observer; + /** + * @tc.steps: step4. param A B disable + * @tc.expected: step4. notifier WRITE_CLOSED + */ + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierB); + EXPECT_TRUE(errCode == E_OK); + errCode = RuntimeContext::GetInstance()->DisableKvStoreAutoLaunch(g_identifierC); + EXPECT_TRUE(errCode == E_OK); + g_communicatorAggregator->RunOnConnectCallback(REMOTE_DEVICE_ID, false); +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.cpp new file mode 100755 index 000000000..7ff688c64 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.h new file mode 100755 index 000000000..99d5489bf --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_data_generate_unit_test.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 DISTRIBUTEDDB_DATA_GENERATE_UNIT_H +#define DISTRIBUTEDDB_DATA_GENERATE_UNIT_H + +#include +#include +#include "distributeddb_tools_unit_test.h" +#include "types.h" + +namespace DistributedDBUnitTest { +// define some variables to init a KvStoreDelegateManager object. +const std::string APP_ID = "app0"; +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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp new file mode 100755 index 000000000..6586ce0e1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_json_precheck_unit_test.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "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); +} + +/** + * @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.Level0) +{ + /** + * @tc.steps: step1. Check legal json string with nesting depth of 12. + * @tc.expected: step1. return value = 12. + */ + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING1); + 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.Level0) +{ + /** + * @tc.steps: step1. Check legal json string with nesting depth of 6. + * @tc.expected: step1. return value = 6. + */ + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING3); + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @tc.steps: step1. Detect illegal json string with nesting depth greater than 10. + * @tc.expected: step1. return value > 10. + */ + int stepOne = JsonObject::CalculateNestDepth(JSON_STRING5); + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp new file mode 100755 index 000000000..7d90625f7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_notification_chain_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "notification_chain.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace std; + +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) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp new file mode 100755 index 000000000..b064bb604 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_parcel_unit_test.cpp @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ +} + +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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp new file mode 100755 index 000000000..77286bd96 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_object_test.cpp @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "log_print.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(), + KEYWORD_MODE_STRICT.begin(), KEYWORD_MODE_STRICT.end()); + auto iterForCompatible = std::search(resultSchemaStr.begin(), resultSchemaStr.end(), + KEYWORD_MODE_COMPATIBLE.begin(), KEYWORD_MODE_COMPATIBLE.end()); + if (iterForStrict != resultSchemaStr.end()) { + resultSchemaStr.replace(iterForStrict, iterForStrict + KEYWORD_MODE_STRICT.size(), + KEYWORD_MODE_COMPATIBLE.begin(), KEYWORD_MODE_COMPATIBLE.end()); + return resultSchemaStr; + } + if (iterForCompatible != resultSchemaStr.end()) { + resultSchemaStr.replace(iterForCompatible, iterForCompatible + KEYWORD_MODE_COMPATIBLE.size(), + KEYWORD_MODE_STRICT.begin(), KEYWORD_MODE_STRICT.end()); + return resultSchemaStr; + } + return oriSchemaStr; +} +} + +class DistributedDBSchemaObjectTest : public testing::Test { +public: + static void SetUpTestCase(void) {}; + static void TearDownTestCase(void) {}; + void SetUp() {}; + void TearDown() {}; +}; + +/** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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(); + std::string valueBeforeOffset = valueToString.substr(0, beforeOffset.size()); + EXPECT_TRUE(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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_schema_unit_test.cpp new file mode 100755 index 000000000..748ebe658 --- /dev/null +++ b/services/distributeddataservice/libs/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 "schema_utils.h" +#include "db_errno.h" +#include "log_print.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) +{ +} + +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; // 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; // 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp new file mode 100755 index 000000000..308d2964f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "platform_specific.h" + +#include +#include +#include + +#include +#include +#include + +#include "db_common.h" +#include "value_hash_calc.h" +#include "db_constant.h" +#include "generic_single_ver_kv_entry.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 [%d], entryRet key size is [%d]", entryOrg.key.size(), + entryRet.key.size()); + return false; + } + + if (entryOrg.value != entryRet.value) { + LOGD("value not equal, entryOrg value size is [%d], entryRet value size is [%d]", 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 [%d], entriesRet size is [%d]", 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[%d] not match", i); + return false; + } + if (entries1[i].value != entries2[i].value) { + LOGE("IsEntriesEqual failed, value of index[%d] not match", i); + return false; + } + } + + return true; +} + +bool DistributedDBToolsUnitTest::CheckObserverResult(const std::vector &orgEntries, + const std::list &resultLst) +{ + LOGD("orgEntries.size() is [%d], resultLst.size() is [%d]", 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) +{ + 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:%llu.", fileSize); + return -E_UNEXPECTED_DATA; + } + } + + const int sqliteCountPos = 0; + if (!dataFile.seekp(sqliteCountPos)) { + return -E_UNEXPECTED_DATA; + } + uint32_t currentCount = 0x1F1F1F1F; // add the random value to corrupt the head. + for (int i = 0; i < 256; i++) { // 256 is 1024 / 4 times. + if (!dataFile.write(reinterpret_cast(¤tCount), 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) +{ + 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)); + entry->SetWriteTimestamp(entry->GetTimestamp()); + entries.push_back(entry); + } + + int errCode = store->PutSyncData(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 :%lu -- %lu -- %lu -- %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_; +} + +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(); +} +} // namespace DistributedDBUnitTest diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h new file mode 100755 index 000000000..0b01f496c --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.h @@ -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. + */ + +#ifndef DISTRIBUTEDDB_TOOLS_UNIT_TEST_H +#define DISTRIBUTEDDB_TOOLS_UNIT_TEST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kv_store_changed_data.h" +#include "kv_store_delegate_impl.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_observer.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_snapshot_delegate_impl.h" +#include "log_print.h" +#include "types.h" +#include "db_types.h" +#include "sqlite_utils.h" +#include "single_ver_kv_entry.h" +#include "sqlite_single_ver_natural_store.h" + +namespace DistributedDBUnitTest { +static const int DIR_PERMISSION = 0750; + +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) + {} +}; + +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); + + 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); + + 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 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); + +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 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_; +}; +} // namespace DistributedDBUnitTest + +#endif // DISTRIBUTEDDB_TOOLS_UNIT_TEST_H diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp new file mode 100755 index 000000000..2eaf3881b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/evloop_timer_unit_test.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "platform_specific.h" +#include "evloop/include/ievent.h" +#include "evloop/include/ievent_loop.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; // microsecond to millisecond. +} + +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) +{ + /** + * @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 errCode = timer->SetTimeout(counter * TIME_PIECE_1000); + EXPECT_EQ(errCode, 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/process_communicator_test_stub.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/common/process_communicator_test_stub.h new file mode 100644 index 000000000..76a33c374 --- /dev/null +++ b/services/distributeddataservice/libs/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 "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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.cpp new file mode 100755 index 000000000..491612491 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "db_errno.h" +#include "endian_convert.h" +#include "frame_header.h" + +using namespace DistributedDB; + +namespace { + const uint32_t MTU_SIZE = 5 * 1024 * 1024; // 5 M, 1024 is scale +} + +/* + * Override Part + */ +AdapterStub::~AdapterStub() +{ + // Do nothing +} + +int AdapterStub::StartAdapter() +{ + return E_OK; +} + +void AdapterStub::StopAdapter() +{ + // Do nothing +} + +uint32_t AdapterStub::GetMtuSize() +{ + return MTU_SIZE; +} + +uint32_t AdapterStub::GetMtuSize(const std::string &target) +{ + return GetMtuSize(); +} + +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=%d 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_); +} + +/* + * 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_) { + onReceiveHandle_(srcTarget, bytes, length); + } +} + +/* + * 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.h new file mode 100755 index 000000000..5b39c4048 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/adapter_stub.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 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; + 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; + + /* + * 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.cpp new file mode 100755 index 000000000..16f4a564e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.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 "distributeddb_communicator_common.h" +#include +#include "securec.h" +#include "db_errno.h" +#include "log_print.h" +#include "message_transform.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; + 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; + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h new file mode 100644 index 000000000..543ee944f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_common.h @@ -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. + */ + +#ifndef DISTRIBUTEDDB_COMMUNICATOR_COMMON_H +#define DISTRIBUTEDDB_COMMUNICATOR_COMMON_H + +#include +#include +#include +#include "message.h" +#include "adapter_stub.h" +#include "frame_header.h" +#include "communicator_aggregator.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 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp new file mode 100755 index 000000000..5e67864bb --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_deep_test.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "message.h" +#include "db_errno.h" +#include "log_print.h" +#include "serial_buffer.h" +#include "distributeddb_communicator_common.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() +{ + /** + * @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); + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, msgForAB, true, 0); + 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); + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, sendMsgForAB, false, 0); + 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); + int errCode = g_commBC->SendMessage(DEVICE_NAME_C, sendMsgForBC, false, 0); + 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, false, 0); + 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); + int errCode = g_commAB->SendMessage(DEVICE_NAME_B, sendMsgForAB, false, 0); + 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, false, 0); + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_test.cpp new file mode 100755 index 000000000..8b781d55a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_send_receive_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 +#include +#include "message.h" +#include "log_print.h" +#include "db_errno.h" +#include "distributeddb_communicator_common.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() +{ + /** + * @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 +} + +/** + * @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); + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, true, 0); + 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); + errCode = g_commBB->SendMessage(DEVICE_NAME_A, msgForBB, true, 0); + 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); + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, true, 0); + 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); + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, true, 0); + 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); + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, true, 0); + 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); + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, false, 0); + 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); + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, false, 100); // 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(); + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, true, 0); + 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, true, 0); + 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, true, 0); + 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(); + int errCode = g_commBA->SendMessage(DEVICE_NAME_A, msgForBA, true, 0); + 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, true, 0); + 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, true, 0); + 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); + int errCode = g_commAA->SendMessage(DEVICE_NAME_B, msgForAA, false, 0, 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, false, 0, 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); + int errCode = g_commBB->SendMessage(DEVICE_NAME_A, msgForBB, false, 0); + 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, false, 0); + 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); +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp new file mode 100755 index 000000000..6afabe158 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/communicator/distributeddb_communicator_test.cpp @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "db_errno.h" +#include "endian_convert.h" +#include "distributeddb_communicator_common.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() +{ + /** + * @tc.setup: Do nothing + */ +} + +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)->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); + errCode = commAA->SendMessage(DEVICE_NAME_B, msgForAA, true, 0); + 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); \ + errCode = comm##src##label->SendMessage(DEVICE_NAME_##dst, msgFor##src##label, true, 0); \ + 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); \ + errCode = comm##src##label->SendMessage(DEVICE_NAME_##dst, msgFor##src##label, false, 0); \ + 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)->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)->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)->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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp new file mode 100755 index 000000000..ec05ae598 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_auto_launch_test.cpp @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "runtime_context.h" +#include "db_common.h" +#include "db_constant.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "kv_store_delegate_manager.h" +#include "distributeddb_tools_unit_test.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); + } + } + 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) +{ + 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.Level0) +{ + /** + * @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. + for (auto &item : entries) { + kvStore->ReleaseKvEntry(item); + item = nullptr; + } + 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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_syncdb_test.cpp new file mode 100755 index 000000000..7254a1fce --- /dev/null +++ b/services/distributeddataservice/libs/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) +{ + // 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp new file mode 100755 index 000000000..66a6da209 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_data_operation_test.cpp @@ -0,0 +1,1788 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + // 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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); +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp new file mode 100755 index 000000000..eadd167b5 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_corrupt_test.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) { + DBStatus status = kvDelegate->Put(key, value); + if (status != OK) { + return status; + } + } + if (kvNbDelegate != nullptr) { + DBStatus 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) +{ + 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); +} diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp new file mode 100755 index 000000000..0141a8da7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_database_test.cpp @@ -0,0 +1,1351 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "platform_specific.h" +#include "kvdb_manager.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "runtime_context.h" +#include "process_system_api_adapter_impl.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) +{ + if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) { + LOGE("rm test db files error!"); + } + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void DistributedDBInterfacesDatabaseTest::SetUp(void) +{ + g_kvDelegateStatus = INVALID_ARGS; + g_kvDelegatePtr = nullptr; +} + +void DistributedDBInterfacesDatabaseTest::TearDown(void) {} + +/** + * @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_TRUE(g_kvDelegateStatus == DB_ERROR); + + /** + * @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_TRUE(g_kvDelegateStatus == DB_ERROR); + + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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(); + EXPECT_TRUE(g_adapter != nullptr); + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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: 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.Level0) +{ + /** + * @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); + LOGE("[%s]", dataBaseDir.c_str()); + ASSERT_EQ(OS::CheckPathExistence(dataBaseDir), false); +} diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp new file mode 100755 index 000000000..77526cd26 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_device_identifier_test.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "platform_specific.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) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp new file mode 100755 index 000000000..e1a0525a4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_database_test.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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) +{ +} + +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); + 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); + 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); + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_encrypt_delegate_test.cpp new file mode 100755 index 000000000..86e051d96 --- /dev/null +++ b/services/distributeddataservice/libs/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 "sqlite_utils.h" +#include "sqlite_single_ver_natural_store.h" +#include "db_errno.h" +#include "log_print.h" +#include "kv_store_nb_conflict_data.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); + int errCode = g_passwd1.SetValue(passwdBuffer1.data(), passwdBuffer1.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer2(5, 2); + errCode = g_passwd2.SetValue(passwdBuffer2.data(), passwdBuffer2.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer3(5, 3); + 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) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ +} + +/** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_import_and_export_test.cpp new file mode 100755 index 000000000..1837f7d84 --- /dev/null +++ b/services/distributeddataservice/libs/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 + +#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); + int errCode = g_passwd1.SetValue(passwdBuffer1.data(), passwdBuffer1.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer2(5, 2); + errCode = g_passwd2.SetValue(passwdBuffer2.data(), passwdBuffer2.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer3(5, 3); + errCode = g_passwd3.SetValue(passwdBuffer3.data(), passwdBuffer3.size()); + ASSERT_EQ(errCode, CipherPassword::ErrorCode::OK); + vector passwdBuffer4(5, 4); + 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) +{ + 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 + "/jadaksdjadkjsa/" + "/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 + "/jadaksdjadkjsa/" + "/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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp new file mode 100755 index 000000000..971aebcbb --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_index_unit_test.cpp @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sqlite_import.h" +#include "distributeddb_tools_unit_test.h" +#include "query.h" +#include "db_common.h" +#include "db_constant.h" +#include "schema_utils.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 database schema related info + 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 = atoi(*(argv)); + 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() < 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 <= 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 <= 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!"); + } +} + +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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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 schema 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 < SCHEMA_INDEX_COUNT_MAX; serial++) { + FieldPath path = GenerateFieldPath(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.Level0) +{ + 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 schema 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp new file mode 100755 index 000000000..5481cb497 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_local_batch_test.cpp @@ -0,0 +1,1137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "db_constant.h" +#include "log_print.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; + 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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp new file mode 100755 index 000000000..2d39cab2a --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_schema_put_test.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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) +{} + +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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp new file mode 100755 index 000000000..f74645eb4 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_delegate_test.cpp @@ -0,0 +1,1945 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_tools_unit_test.h" +#include "sqlite_utils.h" +#include "sqlite_single_ver_natural_store.h" +#include "db_errno.h" +#include "log_print.h" +#include "db_common.h" +#include "db_constant.h" +#include "kv_store_nb_conflict_data.h" +#include "runtime_context.h" +#include "process_system_api_adapter_impl.h" +#include "platform_specific.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) +{ + 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); +#ifdef HW_USING_LABEL_FUNC_STUB + sqlite3_release_label_info(); + sqlite3_set_lock_status(UNLOCKED); +#endif +} + +/** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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); +} + +#ifdef HW_USING_LABEL_FUNC_STUB +namespace { +void SetSqliteLabel(const std::string &storeId, const SecurityOption &secOption) +{ + std::string identifier = DBCommon::TransferHashString(USER_ID + "-" + APP_ID + "-" + storeId); + std::string identifierDir = DBCommon::TransferStringToHex(identifier); + + std::string s3SeceDir = g_testDir + "/" + identifierDir + "/single_ver/"; + sqlite3_set_label_info((s3SeceDir + "main").c_str(), secOption.securityLabel, secOption.securityFlag); + sqlite3_set_label_info((s3SeceDir + "cache").c_str(), secOption.securityLabel, secOption.securityFlag); + sqlite3_set_label_info((s3SeceDir + "meta").c_str(), SecurityLabel::S2, SecurityFlag::ECE); +} + +void ResetSqliteLabel(std::shared_ptr &adapter) +{ + sqlite3_release_label_info(); + sqlite3_init_label_info(); + if (adapter != nullptr) { + adapter.reset(); + } + + adapter = std::make_shared(); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); +} + +void CreateDiffSecOptDb(const std::string &storeId, LockState lockState = UNLOCKED) +{ + if (lockState == LOCKED) { + LOGD("Is locked state!"); + } + + sqlite3_set_lock_status(lockState); + std::shared_ptr adapter = std::make_shared(); + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(adapter); + + KvStoreNbDelegate::Option option; + SecurityOption secOption{SecurityLabel::S2, SecurityFlag::ECE}; + SetSqliteLabel(storeId, secOption); + option.secOption = secOption; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_mgr.DeleteKvStore(storeId); + ResetSqliteLabel(adapter); + + secOption = {SecurityLabel::S3, SecurityFlag::SECE}; + SetSqliteLabel(storeId, secOption); + option.secOption = secOption; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + std::string identifier = DBCommon::TransferHashString(USER_ID + "-" + APP_ID + "-" + storeId); + std::string hashIdentifier = DBCommon::TransferStringToHex(identifier); + std::string mainDbPath = g_testDir + "/" + hashIdentifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::MAINDB_DIR +"/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string cacheDbPath = g_testDir + "/" + hashIdentifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::CACHEDB_DIR +"/" + DBConstant::SINGLE_VER_CACHE_STORE + DBConstant::SQLITE_DB_EXTENSION; + EXPECT_TRUE(OS::CheckPathExistence(mainDbPath)); + EXPECT_FALSE(OS::CheckPathExistence(cacheDbPath)); + g_mgr.DeleteKvStore(storeId); + ResetSqliteLabel(adapter); + + secOption = {SecurityLabel::S4, SecurityFlag::ECE}; + SetSqliteLabel(storeId, secOption); + option.secOption = secOption; + g_mgr.GetKvStore(storeId, option, g_kvNbDelegateCallback); + if (lockState == LOCKED) { + EXPECT_NE(g_kvDelegateStatus, OK); + return; + } + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + g_mgr.DeleteKvStore(storeId); + ResetSqliteLabel(adapter); +} +} + +/** + * @tc.name: GetKvStoreDbInLockState + * @tc.desc: Test GetKvstore in different security option. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, GetKvStoreInDiffOption, TestSize.Level0) +{ + sqlite3_init_label_info(); + CreateDiffSecOptDb("distributed_nb_Diff_SecOpt_test"); +} + +/** + * @tc.name: GetKvStoreDbInLockState + * @tc.desc: Test GetKvstore in lock state. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, GetKvStoreDbInLockState, TestSize.Level0) +{ + std::shared_ptr adapter = std::make_shared(); + ResetSqliteLabel(adapter); + CreateDiffSecOptDb("GetKvStoreDbInLockState", LOCKED); +} + +/** + * @tc.name: GetKvStoreDbInLockState + * @tc.desc: Test GetKvstore in lock state. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + * @tc.author: sunpeng + */ +HWTEST_F(DistributedDBInterfacesNBDelegateTest, GetKvStoreCreateCacheDb, TestSize.Level0) +{ + std::shared_ptr adapter = std::make_shared(); + ResetSqliteLabel(adapter); + sqlite3_set_lock_status(UNLOCKED); + + std::string identifier = DBCommon::TransferHashString(USER_ID + "-" + APP_ID + "-" + "storeId"); + std::string hashIdentifier = DBCommon::TransferStringToHex(identifier); + std::string mainDbPath = g_testDir + "/" + hashIdentifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::MAINDB_DIR +"/" + DBConstant::SINGLE_VER_DATA_STORE + DBConstant::SQLITE_DB_EXTENSION; + std::string cacheDbPath = g_testDir + "/" + hashIdentifier + "/" + DBConstant::SINGLE_SUB_DIR + "/" + + DBConstant::CACHEDB_DIR +"/" + DBConstant::SINGLE_VER_CACHE_STORE + DBConstant::SQLITE_DB_EXTENSION; + LOGE("Db path is [%s]", mainDbPath.c_str()); + + KvStoreNbDelegate::Option option; + SecurityOption secOption{SecurityLabel::S3, SecurityFlag::SECE}; + SetSqliteLabel("storeId", secOption); + option.secOption = secOption; + g_mgr.GetKvStore("storeId", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_EQ(g_kvDelegateStatus, OK); + g_mgr.CloseKvStore(g_kvNbDelegatePtr); + + EXPECT_TRUE(OS::CheckPathExistence(mainDbPath)); + EXPECT_FALSE(OS::CheckPathExistence(cacheDbPath)); + + sqlite3_set_lock_status(LOCKED); + + g_mgr.GetKvStore("storeId", option, g_kvNbDelegateCallback); + ASSERT_TRUE(g_kvNbDelegatePtr != nullptr); + EXPECT_TRUE(g_kvDelegateStatus == OK); + + EXPECT_TRUE(OS::CheckPathExistence(mainDbPath)); + EXPECT_TRUE(OS::CheckPathExistence(cacheDbPath)); + + EXPECT_EQ(g_mgr.DeleteKvStore("storeId"), BUSY); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + EXPECT_EQ(g_mgr.DeleteKvStore("storeId"), OK); + + EXPECT_FALSE(OS::CheckPathExistence(mainDbPath)); + EXPECT_FALSE(OS::CheckPathExistence(cacheDbPath)); +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_publish_test.cpp new file mode 100755 index 000000000..1ccbf3782 --- /dev/null +++ b/services/distributeddataservice/libs/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 "db_errno.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) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_transaction_test.cpp new file mode 100755 index 000000000..ddef73584 --- /dev/null +++ b/services/distributeddataservice/libs/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 "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "db_errno.h" +#include "log_print.h" +#include "db_common.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) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp new file mode 100755 index 000000000..723b9ae15 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_nb_unpublish_test.cpp @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp new file mode 100755 index 000000000..64f71f62e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_query_test.cpp @@ -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. + */ + +#include + +#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 CraetCheckList(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 = CraetCheckList(QueryObjType::NOT_EQUALTO, TEST_FIELD_NAME, 123); + EXPECT_TRUE(CheckQueryContainer(query1, result)); + + Query query2 = Query::Select().EqualTo(TEST_FIELD_NAME, true); + result.clear(); + result = CraetCheckList(QueryObjType::EQUALTO, TEST_FIELD_NAME, true); + EXPECT_TRUE(CheckQueryContainer(query2, result)); + + Query query3 = Query::Select().GreaterThan(TEST_FIELD_NAME, 0); + result.clear(); + result = CraetCheckList(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 = CraetCheckList(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 = CraetCheckList(QueryObjType::GREATER_THAN_OR_EQUALTO, TEST_FIELD_NAME, 1.56); + EXPECT_TRUE(CheckQueryContainer(query5, result)); + + Query query6 = Query::Select().LessThanOrEqualTo(TEST_FIELD_NAME, 100); // random test data + result.clear(); + result = CraetCheckList(QueryObjType::LESS_THAN_OR_EQUALTO, TEST_FIELD_NAME, 100); + EXPECT_TRUE(CheckQueryContainer(query6, result)); + } +} + +class DistributedDBInterfacesQueryDBTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBInterfacesQueryDBTest::SetUpTestCase(void) +{ +} + +void DistributedDBInterfacesQueryDBTest::TearDownTestCase(void) +{ +} + +void DistributedDBInterfacesQueryDBTest::SetUp(void) +{ +} + +void DistributedDBInterfacesQueryDBTest::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(DistributedDBInterfacesQueryDBTest, Query001, TestSize.Level0) +{ + 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 = CraetCheckList(QueryObjType::LIKE, TEST_FIELD_NAME, testValue); + EXPECT_TRUE(CheckQueryContainer(query7, result)); + + Query query8 = Query::Select().NotLike(TEST_FIELD_NAME, "testValue"); + result.clear(); + result = CraetCheckList(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 = CraetCheckList(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(DistributedDBInterfacesQueryDBTest, Query002, TestSize.Level0) +{ + 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(DistributedDBInterfacesQueryDBTest, Query003, TestSize.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp new file mode 100755 index 000000000..aa3af7ec7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_register_syncdb_test.cpp @@ -0,0 +1,1872 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "kv_store_observer.h" +#include "securec.h" +#include "platform_specific.h" +#include "db_common.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) +{ + /* + * 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 snaphot 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 snaphot 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 snaphot 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp new file mode 100755 index 000000000..4ca43857b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_resultset_performance.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "types.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.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 { + // 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 = 10000; + 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) +{ + 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 = 5000; // offset 5000 + LOGI("######## Query resultSet"); + Query query = Query::Select().PrefixKey(keyGet).Limit(128, offset); // limit 128 + EXPECT_EQ(g_kvNbDelegatePtr->GetEntries(query, readResultSet), OK); + ASSERT_TRUE(readResultSet != nullptr); + LOGI("######## After get resultset"); + int totalCount = readResultSet->GetCount(); + EXPECT_EQ(totalCount, 128); // limit 128 + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.cpp new file mode 100755 index 000000000..f02670563 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_schema_database_upgrade_test.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. + */ + +#ifndef OMIT_JSON +#include +#include "kv_store_delegate_manager.h" +#include "distributeddb_tools_unit_test.h" +#include "schema_utils.h" +#include "schema_object.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(), KEYWORD_MODE_STRICT.begin(), + KEYWORD_MODE_STRICT.end()); + auto iterCompatible = std::search(resStr.begin(), resStr.end(), KEYWORD_MODE_COMPATIBLE.begin(), + KEYWORD_MODE_COMPATIBLE.end()); + if (iterStrict != resStr.end()) { + resStr.replace(iterStrict, iterStrict + KEYWORD_MODE_STRICT.size(), KEYWORD_MODE_COMPATIBLE.begin(), + KEYWORD_MODE_COMPATIBLE.end()); + return resStr; + } + if (iterCompatible != resStr.end()) { + resStr.replace(iterCompatible, iterCompatible + KEYWORD_MODE_COMPATIBLE.size(), KEYWORD_MODE_STRICT.begin(), + 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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + // Compatible schema can increase field, but must not be null without defaut. + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_single_version_result_set_test.cpp new file mode 100755 index 000000000..eea5641f4 --- /dev/null +++ b/services/distributeddataservice/libs/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 "distributeddb_data_generate_unit_test.h" +#include "kv_store_nb_delegate_impl.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "db_common.h" +#include "db_constant.h" +#include "db_types.h" +#include "result_entries_window.h" +#include "ikvdb_raw_cursor.h" +#include "kvdb_manager.h" +#include "sqlite_local_kvdb_connection.h" +#include "sqlite_single_ver_forward_cursor.h" +#include "platform_specific.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 DistributedDBSingleVersionResultSetTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributedDBSingleVersionResultSetTest::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 DistributedDBSingleVersionResultSetTest::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 DistributedDBSingleVersionResultSetTest::SetUp(void) +{ + 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 DistributedDBSingleVersionResultSetTest::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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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(DistributedDBSingleVersionResultSetTest, 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.cpp new file mode 100755 index 000000000..a6b2c6d8b --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_space_management_test.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. + */ + +#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) +{ + 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) +{ + int size = 0; + for (const auto &file : fileNames) { + FILE *fileHandle = nullptr; + fileHandle = fopen(file.c_str(), "rb"); + if (fileHandle == nullptr) { + LOGE("Open file[%s] fail", file.c_str()); + continue; + } + (void)fseek(fileHandle, 0, SEEK_END); + size += ftell(fileHandle); + LOGD("CheckRealFileSize:FileName[%s],size[%lld]", file.c_str(), ftell(fileHandle)); + (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.Level1) +{ + 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. + */ + 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:%lld,mul:%lld", 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_optimization_test.cpp new file mode 100755 index 000000000..e525ae9bf --- /dev/null +++ b/services/distributeddataservice/libs/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_errno.h" +#include "db_constant.h" +#include "log_print.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 { + 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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_test.cpp new file mode 100755 index 000000000..920c7867f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_syncdb_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 "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "distributeddb_interfaces_transaction_testcase.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) +{ + /* + * 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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(DistributedDBInterfacesTransactionSyncDBTest, RollBack005, TestSize.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_test.cpp new file mode 100755 index 000000000..c73819248 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_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 "distributeddb_interfaces_transaction_testcase.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 { + 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) +{ + /* + * 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.cpp new file mode 100755 index 000000000..ff6786cf7 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_transaction_testcase.h new file mode 100644 index 000000000..73181f1a4 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp new file mode 100755 index 000000000..29eb8fb41 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "log_print.h" +#include "platform_specific.h" +#include "distributeddb_tools_unit_test.h" + +namespace DistributedDB { +ProcessSystemApiAdapterImpl::ProcessSystemApiAdapterImpl() + : callback_(nullptr), + isLocked_(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 +{ + return true; +} + +void ProcessSystemApiAdapterImpl::SetLockStatus(bool isLock) +{ + std::lock_guard lock(adapterlock_); + if (callback_) { + callback_(isLock); + } + isLocked_ = isLock; +} + +void ProcessSystemApiAdapterImpl::ResetSecOptDic() +{ + pathSecOptDic_.clear(); +} + +void ProcessSystemApiAdapterImpl::ResetAdapter() +{ + ResetSecOptDic(); + SetLockStatus(false); +} +}; diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.h new file mode 100755 index 000000000..6e82fbbe1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/process_system_api_adapter_impl.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 PROCESS_SYSTEM_API_ADAPTER_H +#define PROCESS_SYSTEM_API_ADAPTER_H + +#include +#include +#include + +#include "iprocess_system_api_adapter.h" +#include "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 ResetSecOptDic(); + void ResetAdapter(); + +private: + mutable std::mutex adapterlock_; + OnAccessControlledEvent callback_; + std::map pathSecOptDic_; + bool isLocked_; +}; +} // namespace DistributedDB + +#endif diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp new file mode 100755 index 000000000..74b336002 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/interfaces/runtime_context_process_system_api_adapter_impl_test.cpp @@ -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. + */ + +#include + +#include "db_errno.h" +#include "log_print.h" +#include "runtime_context.h" +#include "iprocess_system_api_adapter.h" +#include "process_system_api_adapter_impl.h" +#include "lock_status_observer.h" + +using namespace testing::ext; +using namespace DistributedDB; +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; +} + +class RuntimeContextProcessSystemApiAdapterImplTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); +}; + +void RuntimeContextProcessSystemApiAdapterImplTest::SetUpTestCase(void) +{ + /** + * @tc.setup: Get a adapter + */ + g_adapter = std::make_shared(); + EXPECT_TRUE(g_adapter != nullptr); +} + +void RuntimeContextProcessSystemApiAdapterImplTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetProcessSystemApiAdapter(nullptr); +} + +void RuntimeContextProcessSystemApiAdapterImplTest::SetUp(void) +{ + g_adapter->ResetAdapter(); +} + +/** + * @tc.name: SetSecurityOption001 + * @tc.desc: Set SecurityOption. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, SetSecurityOption001, TestSize.Level0) +{ + /** + * @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_TRUE(errCode == E_OK); +} + +/** + * @tc.name: GetSecurityOption001 + * @tc.desc: Get SecurityOption. + * @tc.type: FUNC + * @tc.require: AR000EV1G2 + */ +HWTEST_F(RuntimeContextProcessSystemApiAdapterImplTest, GetSecurityOption001, TestSize.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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); +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp new file mode 100755 index 000000000..cde43b856 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_file_package_test.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "securec.h" +#include "package_file.h" +#include "db_errno.h" +#include "platform_specific.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) +{ + (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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + // 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp new file mode 100755 index 000000000..d8b9e3e3e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_multi_ver_vacuum_test.cpp @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "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() +{ +} + +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, 10, 100); // 10 time, 100 ms + EXPECT_EQ(stepOne, true); + stepOne = CheckVacuumTaskStatus(vacuum, DB_IDENTITY_B, VacuumTaskStatus::FINISH, 10, 500); // 10 time, 500 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_sqlite_register_test.cpp new file mode 100755 index 000000000..fb6e55927 --- /dev/null +++ b/services/distributeddataservice/libs/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 "sqlite_import.h" +#include "platform_specific.h" +#include "distributeddb_tools_unit_test.h" +#include "sqlite_local_kvdb_connection.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 *DB_NAME = "test.db"; + const char *SQL_HASH = "SELECT CALC_HASH_KEY(KEY) FROM ADDRESS_TEST"; +#ifndef OMIT_JSON + const char *SQL_JSON_RIGHT = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '$.population', 0) > 800000"; + const char *SQL_JSON_WRONG_PATH = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '.populationWrong', 0) > 800000"; + const char *SQL_JSON_WRONG_ARGS = "SELECT * FROM ADDRESS_TEST \ + WHERE JSON_EXTRACT_BY_PATH(VALUE, '$.population') > 800000"; +#endif + const char *SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS ADDRESS_TEST(" \ + "KEY BLOB NOT NULL PRIMARY KEY," \ + "VALUE BLOB NOT NULL);"; + + const char *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() +{ +} + +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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_commit_storage_test.cpp new file mode 100755 index 000000000..7fdce2836 --- /dev/null +++ b/services/distributeddataservice/libs/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 "distributeddb_tools_unit_test.h" +#include "securec.h" +#include "ikvdb_factory.h" +#include "default_factory.h" +#include "db_errno.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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_data_operation_test.cpp new file mode 100755 index 000000000..08896c94b --- /dev/null +++ b/services/distributeddataservice/libs/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_tools_unit_test.h" +#include "kvdb_manager.h" +#include "sqlite_local_kvdb_connection.h" +#include "distributeddb_data_generate_unit_test.h" +#include "multi_ver_natural_store_transfer_data.h" +#include "multi_ver_natural_store_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) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp new file mode 100755 index 000000000..ebe4c27d1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_encrypt_test.cpp @@ -0,0 +1,1373 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_types.h" +#include "log_print.h" +#include "sqlite3.h" +#include "securec.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'}; + + 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"; + + 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 *errMsg = nullptr; + int errCode = sqlite3_exec(g_db, CREATE_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + + return errCode; + } + + int SetEncryptParam(const char *passwd, int iterNumber, const string &algName) + { + char *errMsg = nullptr; + int errCode = sqlite3_key(g_db, static_cast(passwd), strlen(passwd)); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + + errCode = sqlite3_exec(g_db, (PRAGMA_KDF_ITER + to_string(iterNumber)).c_str(), nullptr, nullptr, + &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + + errCode = sqlite3_exec(g_db, (PRAGMA_CIPHER + algName + ";").c_str(), nullptr, nullptr, + &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + 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 *errMsg = 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, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int DeleteValue(const Key &key) + { + char *errMsg = nullptr; + string keyStr(key.begin(), key.end()); + int errCode = sqlite3_exec(g_db, ("DELETE FROM data WHERE key='" + keyStr + "';").c_str(), nullptr, + nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int UpdateValue(const Key &key, const Value &value) + { + char *errMsg = 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, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int GetValue(const Key &key, Value &value) + { + char *errMsg = 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), &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int Export(const string &dbName) + { + char *errMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("SELECT " + EXPORT_STRING + "('" + dbName + "');").c_str(), nullptr, nullptr, + &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int Attach(const string &dbName) + { + char *errMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("attach '" + dbName + ".db' as " + dbName + " key '';").c_str(), + nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + return errCode; + } + return errCode; + } + + int AttachWithKey(const string &dbName, const char *passwd) + { + char *errMsg = nullptr; + int errCode = sqlite3_exec(g_db, ("attach '" + dbName + ".db' as " + dbName + " key '" + passwd + "';").c_str(), + nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE(" [SQLITE]: %s", errMsg); + sqlite3_free(errMsg); + 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) +{ + /** + * @tc.Clean DB files created from every test case. + */ + remove((STORE_ID + ".db").c_str()); + remove((STORE_ID2 + ".db").c_str()); +} + +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. + */ + remove((STORE_ID + ".db").c_str()); + remove((STORE_ID2 + ".db").c_str()); + /** + * @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.Level0) +{ + /** + * @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); +} + +/** + * @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.Level0) +{ + /** + * @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); +} + +/** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @tc.steps:step1. Open a database without password. + * @tc.expected: step1. Return SQLITE_OK. + */ + EXPECT_EQ(OpenWithKey(g_oldPasswd, ITERATION, ALG1, false), 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); + + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp new file mode 100755 index 000000000..14bc10c74 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_index_optimize_test.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "sqlite_import.h" +#include "platform_specific.h" +#include "types.h" +#include "db_common.h" +#include "db_constant.h" +#include "distributeddb_tools_unit_test.h" +#include "distributeddb_data_generate_unit_test.h" +#include "log_print.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 = userId + "-" + appId + "-" + storeId; + 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) +{ +} + +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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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); + int errCode; + EXPECT_TRUE(queryObject1.IsValid(errCode)); + + /** + * @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); + EXPECT_FALSE(queryObject2.IsValid(errCode)); + + /** + * @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); + EXPECT_FALSE(queryObject3.IsValid(errCode)); +} + +/** + * @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.Level0) +{ + /** + * @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); + std::string sql1; + ASSERT_EQ(queryObject1.GetQuerySql(sql1, false), E_OK); + size_t pos = sql1.find("INDEXED BY '$.id'", 0); + ASSERT_TRUE(pos != std::string::npos); + + /** + * @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(g_kvNbDelegatePtr->GetEntries(query1, entries), OK); + EXPECT_TRUE(value == entries[0].value); + EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK); + g_kvNbDelegatePtr = nullptr; + + /** + * @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); + std::string sql3; + ASSERT_EQ(queryObject3.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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp new file mode 100755 index 000000000..8c0a8a40e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_memory_single_ver_naturall_store_test.cpp @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp new file mode 100755 index 000000000..87024a6a9 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_conflict_test.cpp @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kv_store_nb_conflict_data.h" +#include "kv_store_nb_delegate_impl.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "time_helper.h" +#include "kvdb_conflict_entry.h" +#include "db_errno.h" +#include "db_common.h" +#include "db_constant.h" +#include "platform_specific.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) +{ + /* + * 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp new file mode 100755 index 000000000..2f4b2afe1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_register_observer_test.cpp @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "ikvdb_factory.h" +#include "default_factory.h" +#include "db_constant.h" +#include "db_errno.h" +#include "sqlite_single_ver_natural_store.h" +#include "db_common.h" +#include "log_print.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) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp new file mode 100755 index 000000000..ab4af5f4f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_resultset_and_json_optimize.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "types.h" +#include "sqlite_utils.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "distributeddb_data_generate_unit_test.h" +#include "res_finalizer.h" +#include "db_common.h" +#include "db_constant.h" +#include "platform_specific.h" +#include "sqlite_single_ver_result_set.h" +#include "kvdb_manager.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) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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(); +} diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp new file mode 100755 index 000000000..3297365b3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.cpp @@ -0,0 +1,1847 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "time_helper.h" +#include "generic_single_ver_kv_entry.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: 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; // 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; // 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); // random size + key3 = key2 = key1; + key2.push_back('C'); + key3.pop_back(); + DistributedDBToolsUnitTest::GetRandomKeyValue(value1, 84); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value2, 101); // random size + DistributedDBToolsUnitTest::GetRandomKeyValue(value3, 37); // 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; // 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; // 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); + 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); + 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); +} + +/** + * @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); + 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); + 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)); // 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); // random + EXPECT_EQ(store->PutMetaData(sizeKey, sizeValue), -E_INVALID_ARGS); + sizeKey.pop_back(); + sizeValue.push_back(174); // random + EXPECT_EQ(store->PutMetaData(sizeKey, sizeValue), -E_INVALID_ARGS); +} + +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); // 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); +} + diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.h new file mode 100755 index 000000000..b925c7c9f --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_natural_store_testcase.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. + */ + +#include +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "sqlite_utils.h" +#include "sqlite_single_ver_natural_store.h" +#include "sqlite_single_ver_natural_store_connection.h" +#include "sqlite_local_kvdb.h" +#include "multi_ver_natural_store.h" +#include "db_errno.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 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 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp new file mode 100755 index 000000000..4112fba82 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_single_ver_upgrade_test.cpp @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "distributeddb_tools_unit_test.h" +#include "sqlite_utils.h" +#include "sqlite_single_ver_natural_store.h" +#include "db_errno.h" +#include "log_print.h" +#include "db_common.h" +#include "db_constant.h" +#include "time_helper.h" +#include "platform_specific.h" +#include "iprocess_system_api_adapter.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 { + // 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 == true) { + CheckSecOpt(secopt); + } + if (putCheck == true) { + 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(); + EXPECT_TRUE(g_adapter != nullptr); + 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) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp new file mode 100755 index 000000000..8bdc40a7c --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_sqlite_single_ver_natural_store_test.cpp @@ -0,0 +1,1055 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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::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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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: 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_data_test.cpp new file mode 100755 index 000000000..3ad8b2603 --- /dev/null +++ b/services/distributeddataservice/libs/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_errno.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 "default_factory.h" +#include "sqlite_multi_ver_data_storage.h" +#include "sqlite_utils.h" +#include "db_constant.h" +#include "process_communicator_test_stub.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); // 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::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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + /** + * @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.Level0) +{ + 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:%llu, TimeCommit:%llu", *(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:%llu, TimeSecond:%llu", 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:%llu, TimeCommit:%llu", 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp new file mode 100755 index 000000000..abc661125 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/distributeddb_storage_transaction_record_test.cpp @@ -0,0 +1,1100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "sqlite_local_kvdb_connection.h" +#include "sqlite_multi_ver_data_storage.h" +#include "sqlite_utils.h" +#include "db_constant.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) +{ + 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; + } + + remove(g_storeDir.c_str()); +} + +/** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.cpp new file mode 100755 index 000000000..9a69f282f --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/storage/multi_ver_vacuum_executor_stub.h new file mode 100755 index 000000000..a96f02aa2 --- /dev/null +++ b/services/distributeddataservice/libs/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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp new file mode 100755 index 000000000..7eace5ee1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_ability_sync_test.cpp @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "version.h" +#include "sync_types.h" +#include "vitural_communicator_aggregator.h" +#include "vitural_communicator.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "single_ver_sync_task_context.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; +} + +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) +{ + /** + * @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); +} + +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; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_BASE); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetSendCode(E_OK); + 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_BASE); + EXPECT_TRUE(packet2->GetSendCode() == E_OK); + std::string schema; + packet2->GetSchema(schema); + 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_RELEASE_3_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_3_0); + EXPECT_TRUE(packet2->GetSendCode() == E_OK); + std::string schema; + packet2->GetSchema(schema); + 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(schema); + 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; + packet1.SetProtocolVersion(ABILITY_SYNC_VERSION_V1); + packet1.SetSoftwareVersion(SOFTWARE_VERSION_CURRENT); + packet1.SetSchema(TEST_SCHEMA); + packet1.SetAckCode(E_VERSION_NOT_SUPPORT); + 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); + std::string schema; + packet2->GetSchema(schema); + 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, 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, 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) SingleVerSyncTaskContext(); + 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, 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) SingleVerSyncTaskContext(); + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp new file mode 100755 index 000000000..e3b5f9d86 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_anti_dos_sync_test.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "message.h" +#include "meta_data.h" +#include "ref_object.h" +#include "single_ver_sync_engine.h" +#include "single_ver_data_sync.h" +#include "vitural_communicator_aggregator.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "version.h" +#include "generic_single_ver_kv_entry.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"; + const int NUM = 108; + 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) +{ + /** + * @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); + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp new file mode 100755 index 000000000..920bd7fee --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_multi_ver_p2p_sync_test.cpp @@ -0,0 +1,1652 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "kv_store_observer.h" +#include "kv_store_delegate.h" +#include "vitural_communicator_aggregator.h" +#include "vitural_communicator.h" +#include "vitural_device.h" +#include "isyncer.h" +#include "virtual_multi_ver_sync_db_interface.h" +#include "time_sync.h" +#include "meta_data.h" +#include "kvdb_manager.h" +#include "kvdb_pragma.h" +#include "ikvdb_connection.h" +#include "sync_types.h" +#include "commit_history_sync.h" +#include "log_print.h" +#include "multi_ver_data_sync.h" +#include "platform_specific.h" +#include "db_common.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_stroe_sync_test"; + const string STORE_ID_A = "kv_stroe_sync_test_a"; + const string STORE_ID_B = "kv_stroe_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; + VituralDevice* g_deviceB = nullptr; + VituralDevice* 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) +{ + /** + * @tc.setup: create virtual device B and C + */ + g_communicatorAggregator->Disable(); + g_deviceB = new (std::nothrow) VituralDevice(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) VituralDevice(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) > 0); + 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) > 0); + 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) > 0); + 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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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.Level0) +{ + /** + * @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 surport 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp new file mode 100755 index 000000000..9b160250e --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_check_test.cpp @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "vitural_communicator_aggregator.h" +#include "vitural_device.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "platform_specific.h" +#include "process_system_api_adapter_impl.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"; + + 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; + VituralDevice* g_deviceB = nullptr; + VituralDevice* 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 = 6; // 6 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(); + EXPECT_TRUE(g_adapter != nullptr); + 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) +{ + /** + * @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) VituralDevice(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) VituralDevice(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; + } +} + +/** + * @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 6 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 for deivec B + g_deviceB->PutData(entries[i].key, entries[i].value, 0, 0); + } else if (i % 3 == 1) { // 1 4 for device C + g_deviceC->PutData(entries[i].key, entries[i].value, 0, 0); + } else { // 2 5 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 revice 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 revice 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); +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp new file mode 100755 index 000000000..6a8c06b39 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_single_ver_p2p_sync_test.cpp @@ -0,0 +1,2034 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "kv_store_observer.h" +#include "kv_store_nb_delegate.h" +#include "vitural_communicator_aggregator.h" +#include "vitural_communicator.h" +#include "vitural_device.h" +#include "isyncer.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "time_sync.h" +#include "platform_specific.h" +#include "db_constant.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; + VituralDevice* g_deviceB = nullptr; + VituralDevice* 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) +{ + /** + * @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) VituralDevice(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) VituralDevice(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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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.Level0) +{ + 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: 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); +} diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.cpp new file mode 100755 index 000000000..12cadb291 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_syncer_device_manager_test.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. + */ + +#include +#include + +#include "vitural_communicator_aggregator.h" +#include "vitural_communicator.h" +#include "vitural_device.h" +#include "device_manager.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "log_print.h" +#include "parcel.h" +#include "sync_types.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; + VituralDevice* g_deviceB = nullptr; + VituralDevice* 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) +{ + /** + * @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); + g_virtualCommunicator->RegOnConnectCallback( + std::bind(&DeviceManager::OnDeviceConnectCallback, g_deviceManager, + std::placeholders::_1, std::placeholders::_2), nullptr); + + g_deviceB = new (std::nothrow) VituralDevice(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) VituralDevice(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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp new file mode 100755 index 000000000..434f89c50 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/distributeddb_time_sync_test.cpp @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generate_unit_test.h" +#include "virtual_time_sync_communicator.h" +#include "isyncer.h" +#include "virtual_single_ver_sync_db_Interface.h" +#include "sync_types.h" +#include "single_ver_sync_state_machine.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) +{ + /** + * @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) SingleVerSyncTaskContext(); + 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); + 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); + 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); + + 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); + errCode = g_virtualCommunicator->SendMessage(DEVICE_B, msg, false, 0); + 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, false, 0); + 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, false, 0); + 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); + errCode = g_virtualCommunicator->SendMessage(DEVICE_A, msg, false, 0); + 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, false, 0); + 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, false, 0); + 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); + EXPECT_TRUE(errCode == -E_TIMEOUT); +} \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.cpp new file mode 100755 index 000000000..d9ee109f9 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.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 "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::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_; +} +} // namespace DistributedDB + diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h new file mode 100755 index 000000000..17695c480 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_multi_ver_sync_db_interface.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "multi_ver_natural_store.h" + +#include "multi_ver_natural_store_connection.h" +#include "distributeddb_tools_unit_test.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; + + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.cpp new file mode 100755 index 000000000..fd500bb87 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.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 "virtual_single_ver_sync_db_Interface.h" + +#include +#include +#include + +#include "db_errno.h" +#include "log_print.h" +#include "meta_data.h" +#include "securec.h" +#include "generic_single_ver_kv_entry.h" + +namespace DistributedDB { +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) +{ + metadata_[key] = value; + 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 %d", 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; +} + +int VirtualSingleVerSyncDBInterface::PutSyncData(std::vector& dataItems, + const std::string &deviceName) +{ + return -E_NOT_SUPPORT; +} + +SchemaObject VirtualSingleVerSyncDBInterface::GetSchemaInfo() const +{ + return schemaObj_; +} + +bool VirtualSingleVerSyncDBInterface::CheckCompatible(const std::string& schema) const +{ + if (schema_.empty() && schema.empty()) { + return true; + } + return (schemaObj_.CompareAgainstSchemaString(schema) == -E_SCHEMA_EQUAL_EXACTLY); +} + +void VirtualSingleVerSyncDBInterface::ReleaseKvEntry(const SingleVerKvEntry *entry) +{ + delete entry; + entry = nullptr; +} + +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 = %llu", stamp); +} + +int VirtualSingleVerSyncDBInterface::RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) +{ + return E_OK; +} + +int VirtualSingleVerSyncDBInterface::GetSyncData(const Key &key, VirtualDataItem &item) +{ + auto iter = std::find_if(dbData_.begin(), dbData_.end(), + [key](VirtualDataItem item) { return item.key == key; }); + if (iter != dbData_.end()) { + item.key = iter->key; + item.value = iter->value; + item.timeStamp = iter->timeStamp; + item.writeTimeStamp = iter->writeTimeStamp; + item.flag = iter->flag; + item.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; + } + for (auto item : dataItems) { + GenericSingleVerKvEntry *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::GetSyncDataNext(std::vector &entries, + ContinueToken &continueStmtToken, const DataSizeSpecInfo &dataSizeInfo) const +{ + if (continueStmtToken == nullptr) { + return -E_NOT_SUPPORT; + } + return 0; +} + +int VirtualSingleVerSyncDBInterface::PutSyncData(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 genricKvEntry = static_cast(kvEntry); + VirtualDataItem item; + genricKvEntry->GetKey(item.key); + genricKvEntry->GetValue(item.value); + item.timeStamp = genricKvEntry->GetTimestamp(); + item.writeTimeStamp = genricKvEntry->GetWriteTimestamp(); + item.flag = genricKvEntry->GetFlag(); + item.isLocal = false; + dataItems.push_back(item); + } + return PutSyncData(dataItems, deviceName); +} + +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 %d", 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 %llu, remote %llu", 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 %llu", 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 +{ +} +} // namespace DistributedDB diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h new file mode 100755 index 000000000..20b5215ec --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_single_ver_sync_db_Interface.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "single_ver_kvdb_sync_interface.h" +#include "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 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; + + int PutSyncData(std::vector& dataItems, const std::string &deviceName) override; + + void ReleaseKvEntry(const SingleVerKvEntry *entry) override; + + void GetMaxTimeStamp(TimeStamp& stamp) const override; + + int RemoveDeviceData(const std::string &deviceName, bool isNeedNotify) override; + + int GetSyncData(const Key& key, VirtualDataItem& item); + + 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 GetSyncDataNext(std::vector &entries, ContinueToken &continueStmtToken, + const DataSizeSpecInfo &dataSizeInfo) const override; + + int PutSyncData(const std::vector &entries, const std::string &deviceName) override; + + SchemaObject GetSchemaInfo() const override; + + bool CheckCompatible(const std::string& schema) 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; + +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); + + std::map, std::vector> metadata_; + std::vector dbData_; + std::string schema_; + SchemaObject schemaObj_; + KvDBProperties properties_; + uint64_t saveDataDelayTime_; + SecurityOption secOption_; +}; +} // namespace DistributedDB + +#endif // KVDB_SYNCABLE_TEST_H \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp new file mode 100755 index 000000000..21d10f445 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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(); +} + +// 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, bool nonBlock, + uint32_t timeout) +{ + return SendMessage(dstTarget, inMsg, nonBlock, timeout, nullptr); +} + +int VirtualTimeSyncCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, + bool nonBlock, uint32_t timeout, 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.h new file mode 100644 index 000000000..ccc6bd7c1 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/virtual_time_sync_communicator.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 VIRTUAL_TIME_SYNC_COMMUNICATOR_H +#define VIRTUAL_TIME_SYNC_COMMUNICATOR_H + +#include +#include +#include +#include +#include +#include + +#include "db_types.h" +#include "ref_object.h" +#include "serial_buffer.h" +#include "icommunicator.h" +#include "communicator_aggregator.h" +#include "vitural_device.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; + // Get local target name for identify self + int GetLocalIdentity(std::string &outTarget) const override; + + int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout, + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.cpp new file mode 100755 index 000000000..476816ea0 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.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. + */ + +#include "vitural_communicator.h" + +#include "vitural_communicator_aggregator.h" +#include "sync_engine.h" +#include "log_print.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, bool nonBlock, + uint32_t timeout) +{ + return SendMessage(dstTarget, inMsg, nonBlock, timeout, nullptr); +} + +int VirtualCommunicator::SendMessage(const std::string &dstTarget, const Message *inMsg, + bool nonBlock, uint32_t timeout, 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 = 1; + 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(onConnectLock_); + if (isEnable_ && onConnect_ && (target != deviceId_)) { + onConnect_(target, isConnect); + } +} + +uint32_t VirtualCommunicator::GetCommunicatorMtuSize() const +{ + return 5 * 1024 * 1024; // 5M +} + +uint32_t VirtualCommunicator::GetCommunicatorMtuSize(const std::string &target) const +{ + return GetCommunicatorMtuSize(); +} + +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_; +} + +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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.h new file mode 100644 index 000000000..e19254870 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "ref_object.h" +#include "serial_buffer.h" +#include "icommunicator.h" +#include "vitural_device.h" + +namespace DistributedDB { +class VirtualCommunicatorAggregator; + +class VituralDevice; + +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; + int GetLocalIdentity(std::string &outTarget) const override; + + int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout) override; + int SendMessage(const std::string &dstTarget, const Message *inMsg, bool nonBlock, uint32_t timeout, + 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 isConnec) const; + + int GeneralVirtualSyncId(); + + void Disable(); + + void Enable(); + + void SetDeviceId(const std::string &deviceId); + + std::string GetDeviceId() const; + + bool IsEnabled() const; + +private: + int TimeSync(); + int DataSync(); + int WaterMarkSync(); + + mutable std::mutex onMessageLock_; + OnMessageCallback onMessage_; + + mutable std::mutex onConnectLock_; + OnConnectCallback onConnect_; + + 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/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.cpp new file mode 100755 index 000000000..5884b95b7 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "vitural_communicator.h" + +#include +#include + +#include "db_errno.h" +#include "vitural_communicator_aggregator.h" +#include "log_print.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_) { + onCommLack_(commLabel); + } +} + +void VirtualCommunicatorAggregator::RunOnConnectCallback(const std::string &target, bool isConnect) +{ + if (onConnect_) { + onConnect_(target, isConnect); + } +} + +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); + std::thread thread([communicator, srcTarget, msg]() { + communicator->CallbackOnMessage(srcTarget, msg); + RefObject::DecObjRef(communicator); + }); + thread.detach(); + CallSendEnd(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); + }); + } +} +} // namespace DistributedDB + diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.h new file mode 100644 index 000000000..3d87d1747 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_communicator_aggregator.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 VIRTUAL_ICOMMUNICATORAGGREGATOR_H +#define VIRTUAL_ICOMMUNICATORAGGREGATOR_H + +#include + +#include "icommunicator_aggregator.h" +#include "vitural_device.h" +#include "vitural_communicator.h" + +namespace DistributedDB { +class ICommunicator; // Forward Declaration + +class VituralDevice; + +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); + + // 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; + + ~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_; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_ICOMMUNICATORAGGREGATOR_H \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.cpp b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.cpp new file mode 100755 index 000000000..3029a69a3 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "vitural_device.h" + +#include +#include + +#include "virtual_single_ver_sync_db_Interface.h" +#include "virtual_multi_ver_sync_db_interface.h" +#include "vitural_communicator.h" +#include "vitural_communicator_aggregator.h" +#include "single_ver_sync_state_machine.h" +#include "multi_ver_sync_state_machine.h" +#include "device_manager.h" +#include "log_print.h" + +namespace DistributedDB { +VituralDevice::VituralDevice(const std::string &deviceId) + : communicateHandle_(nullptr), + storage_(nullptr), + metadata_(nullptr), + deviceId_(deviceId) +{ +} + +VituralDevice::~VituralDevice() +{ + 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) { + IKvDBSyncInterface *storage = storage_; + context_->OnLastRef([storage, &cv, &cvMutex, &finished]() { + if (storage != nullptr) { + 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 VituralDevice::Initialize(VirtualCommunicatorAggregator *communicatorAggregator, IKvDBSyncInterface *syncInterface) +{ + if ((communicatorAggregator == nullptr) || (syncInterface == nullptr)) { + return -E_INVALID_ARGS; + } + + communicatorAggregator_ = communicatorAggregator; + 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) SingleVerSyncTaskContext; + } else { + context_ = new (std::nothrow) MultiVerSyncTaskContext; + } + if (context_ == nullptr) { + return -E_OUT_OF_MEMORY; + } + communicateHandle_->RegOnMessageCallback(std::bind(&VituralDevice::MessageCallback, this, + std::placeholders::_1, std::placeholders::_2), []() {}); + context_->Initialize(remoteDeviceId_, storage_, metadata_, communicateHandle_); + context_->SetRetryStatus(SyncTaskContext::NO_NEED_RETRY); + context_->RegOnSyncTask(std::bind(&VituralDevice::StartResponseTask, this)); + return E_OK; +} + +void VituralDevice::SetDeviceId(const std::string &deviceId) +{ + deviceId_ = deviceId; +} + +std::string VituralDevice::GetDeviceId() const +{ + return deviceId_; +} + +int VituralDevice::GetData(const Key &key, VirtualDataItem &item) +{ + VirtualSingleVerSyncDBInterface *syncAble = static_cast(storage_); + return syncAble->GetSyncData(key, item); +} + +int VituralDevice::GetData(const Key &key, Value &value) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->GetData(key, value); +} + +int VituralDevice::PutData(const Key &key, const Value &value, const TimeStamp &time, int flag) +{ + VirtualSingleVerSyncDBInterface *syncAble = static_cast(storage_); + LOGI("dev %s put data time %llu", deviceId_.c_str(), time); + return syncAble->PutData(key, value, time, flag); +} + +int VituralDevice::PutData(const Key &key, const Value &value) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->PutData(key, value); +} + +int VituralDevice::DeleteData(const Key &key) +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->DeleteData(key); +} + +int VituralDevice::StartTransaction() +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->StartTransaction(); +} + +int VituralDevice::Commit() +{ + VirtualMultiVerSyncDBInterface *syncInterface = static_cast(storage_); + return syncInterface->Commit(); +} + +int VituralDevice::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("[VituralDevice] onMessag, 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 VituralDevice::OnRemoteDataChanged(const std::function &callback) +{ + onRemoteDataChanged_ = callback; +} + +void VituralDevice::Online() +{ + static_cast(communicateHandle_)->Enable(); + communicatorAggregator_->OnlineDevice(deviceId_); +} + +void VituralDevice::Offline() +{ + static_cast(communicateHandle_)->Disable(); + communicatorAggregator_->OfflineDevice(deviceId_); +} + +int VituralDevice::StartResponseTask() +{ + LOGD("[VituralDevice] StartResponseTask"); + RefObject::AutoLock lockGuard(context_); + int status = context_->GetTaskExecStatus(); + if ((status == SyncTaskContext::RUNNING) || context_->IsKilled()) { + LOGD("[VituralDevice] StartResponseTask status:%d", status); + return -E_NOT_SUPPORT; + } + if (context_->IsTargetQueueEmpty()) { + LOGD("[VituralDevice] StartResponseTask IsTargetQueueEmpty is empty"); + return E_OK; + } + context_->SetTaskExecStatus(ISyncTaskContext::RUNNING); + context_->MoveToNextTarget(); + LOGI("[VituralDevice] machine StartSync"); + context_->UnlockObj(); + int errCode = context_->StartStateMachine(); + context_->LockObj(); + if (errCode != E_OK) { + LOGE("[VituralDevice] machine StartSync failed"); + context_->SetOperationStatus(SyncOperation::FAILED); + } + return errCode; +} + +TimeOffset VituralDevice::GetLocalTimeOffset() const +{ + return metadata_->GetLocalTimeOffset(); +} + +void VituralDevice::SetSaveDataDelayTime(uint64_t milliDelayTime) +{ + VirtualSingleVerSyncDBInterface *syncInterface = static_cast(storage_); + syncInterface->SetSaveDataDelayTime(milliDelayTime); +} + +int VituralDevice::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(); + RefObject::KillAndDecObjRef(operation); + }); + context_->AddSyncOperation(operation); + operation->WaitIfNeed(); + return E_OK; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.h b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.h new file mode 100755 index 000000000..e520a0e74 --- /dev/null +++ b/services/distributeddataservice/libs/distributeddb/test/unittest/common/syncer/vitural_device.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 VIRTUAL_DEVICE_H +#define VIRTUAL_DEVICE_H + +#include "icommunicator.h" + +#include "ikvdb_sync_interface.h" +#include "single_ver_sync_task_context.h" +#include "virtual_single_ver_sync_db_Interface.h" + +namespace DistributedDB { +class VirtualCommunicatorAggregator; + +class VituralDevice { +public: + explicit VituralDevice(const std::string &deviceId); + ~VituralDevice(); + + int Initialize(VirtualCommunicatorAggregator *communicatorAggregator, IKvDBSyncInterface *syncInterface); + void SetDeviceId(const std::string &deviceId); + std::string GetDeviceId() const; + 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(); + int MessageCallback(const std::string &deviceId, Message *inMsg); + void OnRemoteDataChanged(const std::function &callback); + void Online(); + void Offline(); + int StartResponseTask(); + TimeOffset GetLocalTimeOffset() const; + void SetSaveDataDelayTime(uint64_t milliDelayTime); + int Sync(SyncMode mode, bool wait); + +private: + ICommunicator *communicateHandle_; + VirtualCommunicatorAggregator *communicatorAggregator_ = nullptr; + IKvDBSyncInterface *storage_; + std::shared_ptr metadata_; + std::string deviceId_; + std::string remoteDeviceId_ = "real_device"; + SyncTaskContext *context_ = nullptr; + std::function onRemoteDataChanged_; +}; +} // namespace DistributedDB + +#endif // VIRTUAL_DEVICE_H diff --git a/services/distributeddataservice/sa_profile/1301.xml b/services/distributeddataservice/sa_profile/1301.xml new file mode 100644 index 000000000..dd59c9ed7 --- /dev/null +++ b/services/distributeddataservice/sa_profile/1301.xml @@ -0,0 +1,26 @@ + + + + distributeddata + + 1301 + libdistributeddataservice.z.so + true + false + 1 + + + diff --git a/services/distributeddataservice/test/BUILD.gn b/services/distributeddataservice/test/BUILD.gn new file mode 100755 index 000000000..27893e873 --- /dev/null +++ b/services/distributeddataservice/test/BUILD.gn @@ -0,0 +1,516 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 = [ + "../libs/distributeddb/interfaces/include", + "../libs/distributeddb/storage/include", + "../libs/distributeddb/storage/src", + "../libs/distributeddb/storage/src/multiver", + "../libs/distributeddb/storage/src/sqlite", + "../libs/distributeddb/common/include", + "../libs/distributeddb/include", + "../libs/distributeddb/interfaces/src", + "../adapter/include/communicator", + "//developtools/liblog", + "common/distributeddb/include", + "moduletest/common/distributeddb/include", + "//utils/native/base/include", + "//third_party/sqlite/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//third_party/openssl/include/", + ] + + defines = [ + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "USE_SQLITE_SYMBOLS", + "USING_HILOG_LOGGER", + "TESTCASES_USING_GTEST_EXT", + "OMIT_JSON", + "LOW_LEVEL_MEM_DEV", + ] +} + +##############################moduletest########################################## +ohos_moduletest("DistributeddbKvTransactionTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributed_crud_transaction_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_transaction_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbKvTransactionPerfTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributed_crud_transaction_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_transaction_perf_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvConcurrencyCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_concurrency_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvBatchCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_batch_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvCreateTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_create_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvObserverTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_observer_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbKvObserverSnapTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_sysinfo.cpp", + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_observer_snap_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbKvBackupTest") { + module_out_path = module_output_path + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_backup_test.cpp", + ] + configs = [ ":module_private_config" ] + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbKvRealdelTest") { + module_out_path = module_output_path + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "moduletest/common/distributeddb/src/distributeddb_kv_realdel_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbCreateTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_create_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbNbCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} +ohos_moduletest("DistributeddbNbObserverTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_observer_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbCursorTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_cursor_test.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_cursor_testcase.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbBackupTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_backup_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbBatchCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_batch_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbLocalBatchCrudTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_local_batch_crud_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbSchemaDbTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_schema_test.cpp", + ] + configs = [ ":module_private_config" ] + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbPredicateQueryTest") { + module_out_path = module_output_path + + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbEnableSyncByClosedDbTest") { + module_out_path = module_output_path + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_enable_sync_by_closed_db_test.cpp", + ] + configs = [ ":module_private_config" ] + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +ohos_moduletest("DistributeddbNbPredicateQueryExpandTest") { + module_out_path = module_output_path + sources = [ + "common/distributeddb/src/distributed_test_tools.cpp", + "common/distributeddb/src/distributeddb_data_generator.cpp", + "common/distributeddb/src/distributeddb_nb_test_tools.cpp", + "common/distributeddb/src/distributeddb_schema_test_tools.cpp", + "moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_expand_test.cpp", + ] + configs = [ ":module_private_config" ] + + deps = [ + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:distributeddb", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + "//third_party/openssl:libcrypto_static", + ] + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +} + +############################################################################### +group("moduletest") { + testonly = true + deps = [ + "//third_party/googletest:gmock", + "//third_party/googletest:gtest_main", + "//third_party/sqlite:sqlite", + "//utils/native/base:utils", + ] + + deps += [ + ":DistributeddbKvBackupTest", + ":DistributeddbKvBatchCrudTest", + ":DistributeddbKvConcurrencyCrudTest", + ":DistributeddbKvCreateTest", + ":DistributeddbKvCrudTest", + ":DistributeddbKvObserverSnapTest", + ":DistributeddbKvObserverTest", + ":DistributeddbKvRealdelTest", + ":DistributeddbKvTransactionPerfTest", + ":DistributeddbKvTransactionTest", + ":DistributeddbNbBackupTest", + ":DistributeddbNbBatchCrudTest", + ":DistributeddbNbCreateTest", + ":DistributeddbNbCrudTest", + ":DistributeddbNbCursorTest", + ":DistributeddbNbEnableSyncByClosedDbTest", + ":DistributeddbNbLocalBatchCrudTest", + ":DistributeddbNbObserverTest", + ":DistributeddbNbPredicateQueryExpandTest", + ":DistributeddbNbPredicateQueryTest", + ] +} +############################################################################### diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributed_test_sysinfo.h b/services/distributeddataservice/test/common/distributeddb/include/distributed_test_sysinfo.h new file mode 100755 index 000000000..dc89b8ee7 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributed_test_sysinfo.h @@ -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. + */ +#ifndef DISTRIBUTED_TEST_SYSINFO_H +#define DISTRIBUTED_TEST_SYSINFO_H + +#include +#include "platform_specific.h" +#include + +const uint64_t SYSTEM_INFO_BUFFER_SIZE = 20; +const uint64_t PROC_BUFFER_LENGTH = 4096; +const uint64_t DEFAULT_INTEVAL = 250000; +const uint64_t DEFAULT_COUNT = 5; + +const std::string SYS_MEM_FILE = "/proc/meminfo"; +const std::string SYS_CPU_FILE = "/proc/stat"; +const std::string POWER_FOLLOW_FILE = "/sys/class/power_supply/Battery/current_now"; +const std::string VOLTAGE_FILE = "/sys/class/power_supply/Battery/voltage_now"; +const std::string FILE_READ_PERMISSION = "r"; + +// struct of mem occupy +struct MemOccupy { + char memTotalName_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t memTotal_; + char memFreeName_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t memFree_; + char buffersName_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t buffers_; + char cachedName_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t cached_; + char swapCachedName_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t swapCached_; +}; + +// struct of cpu occupy +struct CpuOccupy { + char name_[SYSTEM_INFO_BUFFER_SIZE]; + uint64_t user_; + uint64_t nice_; + uint64_t system_; + uint64_t idle_; + uint64_t iowait_; + uint64_t irq_; + uint64_t softirq_; +}; + +enum SeqNo { + FIRST = 1, + SECOND +}; + +// this class get system info, such as system or cpu storage and power at the moment +class DistributedTestSysInfo final { +public: + + DistributedTestSysInfo(); + ~DistributedTestSysInfo() {} + + // Delete the copy and assign constructors + DistributedTestSysInfo(const DistributedTestSysInfo &distributeTestSysInfo) = delete; + DistributedTestSysInfo& operator=(const DistributedTestSysInfo &distributeTestSysInfo) = delete; + DistributedTestSysInfo(DistributedTestSysInfo &&distributeTestSysInfo) = delete; + DistributedTestSysInfo& operator=(DistributedTestSysInfo &&distributeTestSysInfo) = delete; + + // read memory information from system file + void GetSysMemOccpy(SeqNo seqNo); + // read cpu information from system file sleep microSeconds between first and second read. + void GetSysCpuUsage(SeqNo seqNo, uint64_t microSeconds); + // read value from specific file + float ReadSysValFromFile(const std::string &filePath); + // read value from specific file and take the average within totalCount + float GetSysMeanCurrentVal(const std::string &filePath, int totalCount, uint64_t microSeconds); + // read power from specific file + void GetSysCurrentPower(SeqNo seqNo, int totalCount, uint64_t microSeconds); + + uint64_t GetFirstMemFree() const; + uint64_t GetSecondMemFree() const; + float GetFirstCpuUsage() const; + float GetSecondCpuUsage() const; + float GetFirstPower() const; + float GetSecondPower() const; + + void SaveSecondToFirst(); + +private: + MemOccupy memStatFirst_, memStatSecond_; + CpuOccupy cpuStatFirst_, cpuStatSecond_; + float cpuStatFirstUsage_, cpuStatSecondUsage_; + float powerStatFirst_, powerStatSecond_; + float val_; +}; +#endif // DISTRIBUTED_TEST_SYSINFO_H \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributed_test_tools.h b/services/distributeddataservice/test/common/distributeddb/include/distributed_test_tools.h new file mode 100755 index 000000000..37143c0f0 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributed_test_tools.h @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_MODULE_TEST_TOOLS_H +#define DISTRIBUTED_DB_MODULE_TEST_TOOLS_H +#include +#include + +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" +#include "distributed_test_sysinfo.h" +#include "distributeddb_data_generator.h" +#include "log_print.h" +#ifdef TESTCASES_USING_GTEST +#define HWTEST_F(test_case_name, test_name, level) TEST_F(test_case_name, test_name) +#endif +const int MAX_DIR_LENGTH = 4096; // the max length of directory +const static std::string TAG = "DistributedTestTools"; // for log +const int AUTHORITY = 0755; +const int E_OK = 0; +const int E_ERROR = -1; +const std::string DIRECTOR = "/data/test/getstub/"; // default work dir. +static std::condition_variable g_conditionKvVar; +enum ListType { + INSERT_LIST = 0, + UPDATE_LIST = 1, + DELETE_LIST = 2 +}; + +struct KvDBParameters { + std::string storeId; + std::string appId; + std::string userId; + KvDBParameters(std::string storeIdStr, std::string appIdStr, std::string userIdStr) + : storeId(storeIdStr), appId(appIdStr), userId(userIdStr) + { + } +}; + +struct KvOption { + bool createIfNecessary = true; + bool localOnly = false; + bool isEncryptedDb = false; // whether need encrypt + DistributedDB::CipherType cipher = DistributedDB::CipherType::DEFAULT; // cipher type + std::vector passwd; // cipher password + KvOption(bool createIfNecessary1, bool isLocalOnly, + bool isEncryptedDb1, DistributedDB::CipherType cipher1, std::vector passwd1) + : createIfNecessary(createIfNecessary1), + localOnly(isLocalOnly), + isEncryptedDb(isEncryptedDb1), + cipher(cipher1), passwd(passwd1) + { + } + KvOption() + {} +}; + +struct EncrypteAttribute { + bool isEncryptedDb = false; + std::vector passwd; +}; + +const static KvDBParameters g_kvdbParameter1(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_1); +const static KvDBParameters g_kvdbParameter2(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_2); +const static KvDBParameters g_kvdbParameter3(DistributedDBDataGenerator::STORE_ID_3, + DistributedDBDataGenerator::APP_ID_3, DistributedDBDataGenerator::USER_ID_3); +const static KvDBParameters g_kvdbParameter4(DistributedDBDataGenerator::STORE_ID_4, + DistributedDBDataGenerator::APP_ID_4, DistributedDBDataGenerator::USER_ID_4); +const static KvDBParameters g_kvdbParameter5(DistributedDBDataGenerator::STORE_ID_5, + DistributedDBDataGenerator::APP_ID_5, DistributedDBDataGenerator::USER_ID_5); +const static KvDBParameters g_kvdbParameter6(DistributedDBDataGenerator::STORE_ID_6, + DistributedDBDataGenerator::APP_ID_6, DistributedDBDataGenerator::USER_ID_6); +const static KvDBParameters g_kvdbParameter1_1_2(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_2); +const static KvDBParameters g_kvdbParameter1_2_1(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_1); +const static KvDBParameters g_kvdbParameter1_2_2(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_2); +const static KvDBParameters g_kvdbParameter2_1_1(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_1); +const static KvDBParameters g_kvdbParameter2_1_2(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_2); +const static KvDBParameters KVDB_PARAMETER_PERFORM(DistributedDBDataGenerator::STORE_ID_PERFORM, + DistributedDBDataGenerator::APP_ID_PERFORM, DistributedDBDataGenerator::USER_ID_PERFORM); +const static KvOption g_createKvDiskUnencrypted(true, false, false, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::NULL_PASSWD_VECTOR); +const static KvOption g_createKvDiskEncrypted(true, false, true, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::PASSWD_VECTOR_1); +const static KvOption g_createLocalDiskUnencrypted(true, true, false, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::NULL_PASSWD_VECTOR); +const static KvOption g_createLocalDiskEncrypted(true, true, true, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::PASSWD_VECTOR_1); +static KvOption g_kvOption = g_createKvDiskEncrypted; +bool CompareVector(const std::vector& first, const std::vector& second); +bool CompareList(const std::list& retLst, + const std::list& lst); +bool CompareEntriesVector(std::vector& retVec, + std::vector& expectVec); +void PutUniqueKey(std::vector& entryVec, + const DistributedDB::Key &putKey, const DistributedDB::Value &putValue); +int Uint8VecToString(std::vector& vec, std::string& str); +int GetIntValue(DistributedDB::Value &value); +int RemoveDir(const std::string &directory); +int SetDir(const std::string &directory, const int authRight = AUTHORITY); +void CheckFileNumber(const std::string &filePath, int &fileCount); +DistributedDB::Value GetValueWithInt(int val); +std::vector GenRanKeyVal(int putGetTimes, int keyLength, int valueLength, char val); +std::vector GetKeysFromEntries(std::vector entries, bool random); +bool GetRandBool(); +bool PutEntries(DistributedDB::KvStoreNbDelegate *&delegate, std::vector &entries); + +using SysTime = std::chrono::time_point; +using SysDurTime = std::chrono::duration; + +struct PerformanceData { + int putGetTimes; + int keyLength; + int valueLength; + bool putBatch; + bool getBatch; + bool useClear; + bool getSysInfo; + bool isLocal; + double openDuration; + double putDuration; + double readPutDuration; + double updateDuration; + double readUpdateDuration; + double deleteDuration; + double closeDuration; + PerformanceData(int putGetTimes, int keyLength, int valueLength, + bool putBatch, bool getBatch, bool useClear, bool getSysInfo, bool isLocal) + : putGetTimes(putGetTimes), keyLength(keyLength), valueLength(valueLength), + putBatch(putBatch), getBatch(getBatch), useClear(useClear), + getSysInfo(getSysInfo), isLocal(isLocal), + openDuration(0.0), putDuration(0.0), readPutDuration(0.0), updateDuration(0.0), + readUpdateDuration(0.0), deleteDuration(0.0), closeDuration(0.0) + { + } +}; + +struct Duration { + double putDuration = 0.0; + double readDuration = 0.0; + double updateDuration = 0.0; + double deleteDuration = 0.0; + Duration() = default; + void Clear() + { + putDuration = readDuration = updateDuration = deleteDuration = 0.0; + } +}; + +struct BackupDuration { + double exportDuration = 0.0; + double importDuration = 0.0; + BackupDuration() = default; + BackupDuration(double exportDur, double importDur) + { + exportDuration = exportDur; + importDuration = importDur; + } + BackupDuration operator+(const BackupDuration &backupDur) const + { + return BackupDuration(exportDuration + backupDur.exportDuration, importDuration + backupDur.importDuration); + } + void Clear() + { + exportDuration = importDuration = 0; + } +}; + +enum KvDbType { + ENCRYED = 0, + UNENCRYED = 1 +}; + +enum OperRecordNum { + SINGLE = 1, + SMALL_BATCH = 100, + BATCH = 128, +}; + +enum class OperType { + PUT, + PUT_LOCAL, + UPDATE, + UPDATE_LOCAL, + DELETE, + DELETE_LOCAL +}; + +struct KvPerfData { + KvDbType kvDbType; + int testCnt; + OperRecordNum operRecordNum; + int keyLength; + int valueLength; + bool isPresetRecords; + int presetRecordsCnt; + std::vector allCrudDur; + KvPerfData(KvDbType kvDbType, int testCnt, OperRecordNum operRecordNum, int keyLength, int valueLength, + bool isPresetRecords, int presetRecordsCnt) + : kvDbType(kvDbType), testCnt(testCnt), operRecordNum(operRecordNum), + keyLength(keyLength), valueLength(valueLength), + isPresetRecords(isPresetRecords), presetRecordsCnt(presetRecordsCnt) + { + } +}; + +struct RekeyTypesDur { + double nullPasswdToPasswd1 = 0.0; + double passwd1ToPasswd1 = 0.0; + double passwd1ToPasswd2 = 0.0; + double passwd2ToNullPasswd = 0.0; + RekeyTypesDur() = default; + RekeyTypesDur(double nullPasswdToPasswd1, double passwd1ToPasswd1, + double passwd1ToPasswd2, double passwd2ToNullPasswd) + : nullPasswdToPasswd1(nullPasswdToPasswd1), passwd1ToPasswd1(passwd1ToPasswd1), + passwd1ToPasswd2(passwd1ToPasswd2), passwd2ToNullPasswd(passwd2ToNullPasswd) + { + } + RekeyTypesDur operator+(const RekeyTypesDur &rekeyTypesDur) const + { + return RekeyTypesDur(nullPasswdToPasswd1 + rekeyTypesDur.nullPasswdToPasswd1, + passwd1ToPasswd1 + rekeyTypesDur.passwd1ToPasswd1, + passwd1ToPasswd2 + rekeyTypesDur.passwd1ToPasswd2, + passwd2ToNullPasswd + rekeyTypesDur.passwd2ToNullPasswd); + } +}; + +struct KvRekeyPerfData { + int testCnt; + int presetRecordsCnt; + int keyLength; + int valueLength; + std::vector allRekeyDur; + KvRekeyPerfData(int testCnt, int presetRecordsCnt, int keyLength, int valueLength) + : testCnt(testCnt), presetRecordsCnt(presetRecordsCnt), keyLength(keyLength), valueLength(valueLength) + { + } +}; + +struct KvBackupPerfData { + KvDbType kvDbType; + int testCnt; + int presetRecordsCnt; + int keyLength; + int valueLength; + std::vector allBackupDur; + KvBackupPerfData(KvDbType kvDbType, int testCnt, int presetRecordsCnt, int keyLength, int valueLength) + : kvDbType(kvDbType), testCnt(testCnt), presetRecordsCnt(presetRecordsCnt), + keyLength(keyLength), valueLength(valueLength) + { + } +}; + +struct PerImageGallery { + uint64_t putDuration = 0; + uint64_t readDuration = 0; + void Clear() + { + putDuration = 0; + readDuration = 0; + } +}; +struct NbGalleryPerfData { + int testCnt; + unsigned int keyLength; + unsigned int valueLength; + bool isLocal; + bool isPresetRecords; + int presetRecordsCnt; + std::vector allCrudDur; + NbGalleryPerfData(int testCnt, int keyLength, int valueLength, + bool isLocal, bool isPresetRecords, int presetRecordsCnt) + : testCnt(testCnt), keyLength(keyLength), valueLength(valueLength), + isLocal(isLocal), isPresetRecords(isPresetRecords), presetRecordsCnt(presetRecordsCnt) + { + } +}; +struct NbLocalPerfData { + int testCnt; + unsigned int keyLength; + unsigned int valueLength; + bool isLocal; + bool isPresetRecords; + int presetRecordsCnt; + std::vector allCrudDur; + NbLocalPerfData(int testCnt, int keyLength, int valueLength, + bool isLocal, bool isPresetRecords, int presetRecordsCnt) + : testCnt(testCnt), keyLength(keyLength), valueLength(valueLength), + isLocal(isLocal), isPresetRecords(isPresetRecords), presetRecordsCnt(presetRecordsCnt) + { + } +}; + +struct QueryDur { + double getEntriesDuration = 0.0; + double getResultSetDuration = 0.0; + QueryDur() = default; + void Clear() + { + getEntriesDuration = getResultSetDuration = 0; + } +}; +struct NbSchemaCRUDPerfData { + int testCnt; + OperRecordNum operRecordNum; + unsigned int keyLength; + unsigned int valueLength; + bool isLocal; + bool isPresetRecords; + unsigned int presetRecordsCnt; + bool isQueryNeeded; + bool isIndexSchema; + std::vector allCrudDur; + std::vector allQueryDur; + NbSchemaCRUDPerfData(int testCnt, OperRecordNum operRecordNum, int keyLength, int valueLength, + bool isLocal, bool isPresetRecords, int presetRecordsCnt, int isQueryNeeded, int isIndexSchema) + : testCnt(testCnt), operRecordNum(operRecordNum), keyLength(keyLength), valueLength(valueLength), + isLocal(isLocal), isPresetRecords(isPresetRecords), presetRecordsCnt(presetRecordsCnt), + isQueryNeeded(isQueryNeeded), isIndexSchema(isIndexSchema) + { + } +}; + +// default kvStoreDelegateManager's config. +const DistributedDB::KvStoreConfig KV_CONFIG = { + .dataDir = DIRECTOR +}; + +class DistributedTestTools final { +public: + DistributedTestTools() {} + ~DistributedTestTools() {} + + // Delete the copy and assign constructors + DistributedTestTools(const DistributedTestTools &distributeDBTools) = delete; + DistributedTestTools& operator=(const DistributedTestTools &distributeDBTools) = delete; + DistributedTestTools(DistributedTestTools &&distributeDBTools) = delete; + DistributedTestTools& operator=(DistributedTestTools &&distributeDBTools) = delete; + + // this static method is to compare if the two Value has the same data. + static bool IsValueEquals(const DistributedDB::Value &v1, const DistributedDB::Value &v2); + static DistributedDB::KvStoreDelegate::Option TransferKvOptionType(const KvOption &optionParam); + static DistributedDB::KvStoreDelegate* GetDelegateSuccess(DistributedDB::KvStoreDelegateManager *&outManager, + const KvDBParameters ¶m, const KvOption &optionParam); + static DistributedDB::KvStoreDelegate* GetDelegateStatus(DistributedDB::KvStoreDelegateManager *&outManager, + DistributedDB::DBStatus &status, const KvDBParameters ¶m, const KvOption &optionParam); + + static DistributedDB::DBStatus GetDelegateNotGood( + DistributedDB::KvStoreDelegateManager *&outManager, DistributedDB::KvStoreDelegate *&outDelegate, + const std::string &storeId, const std::string &appId, const std::string &userId, const KvOption &optionParam); + + static DistributedDB::DBStatus Put(DistributedDB::KvStoreDelegate &kvStoreDelegate, + const DistributedDB::Key &key, const DistributedDB::Value &value); + + static DistributedDB::DBStatus PutBatch(DistributedDB::KvStoreDelegate &kvStoreDelegate, + const std::vector &entries); + + static DistributedDB::DBStatus Delete(DistributedDB::KvStoreDelegate &kvStoreDelegate, + const DistributedDB::Key &key); + + static DistributedDB::DBStatus DeleteBatch(DistributedDB::KvStoreDelegate &kvStoreDelegate, + const std::vector &keys); + + static DistributedDB::DBStatus Clear(DistributedDB::KvStoreDelegate &kvStoreDelegate); + + static DistributedDB::KvStoreSnapshotDelegate *GetKvStoreSnapshot(DistributedDB::KvStoreDelegate &kvStoreDelegate); + static DistributedDB::Value Get(DistributedDB::KvStoreDelegate &kvStoreDelegate, const DistributedDB::Key &key); + static DistributedDB::Value Get(DistributedDB::KvStoreSnapshotDelegate &kvStoreSnapshotDelegate, + const DistributedDB::Key &key); + + static std::vector GetEntries( + DistributedDB::KvStoreSnapshotDelegate &kvStoreSnapshotDelegate, const DistributedDB::Key &key); + static std::vector GetEntries(DistributedDB::KvStoreDelegate &kvStoreDelegate, + const DistributedDB::Key &keyPrefix); + + static DistributedDB::KvStoreSnapshotDelegate *RegisterSnapObserver(DistributedDB::KvStoreDelegate *delegate, + DistributedDB::KvStoreObserver *observer); + static DistributedDB::DBStatus RegisterObserver(DistributedDB::KvStoreDelegate *delegate, + DistributedDB::KvStoreObserver *observer); + static DistributedDB::DBStatus UnRegisterObserver(DistributedDB::KvStoreDelegate *delegate, + DistributedDB::KvStoreObserver *observer); + static bool CalculateOpenPerformance(PerformanceData &performanceData); + static bool CalculateInsertPerformance(PerformanceData &performanceData); + static bool CalculateGetPutPerformance(PerformanceData &performanceData); + static bool CalculateUpdatePerformance(PerformanceData &performanceData); + static bool CalculateGetUpdatePerformance(PerformanceData &performanceData); + static bool CalculateUseClearPerformance(PerformanceData &performanceData); + static bool CalculateTransactionPerformance(PerformanceData &performanceData); + static bool CloseAndRelease(DistributedDB::KvStoreDelegateManager *&manager, + DistributedDB::KvStoreDelegate *&delegate); + static bool GetRecordCntByKey(const std::string &dbName, + const std::string &strSql, std::vector &sqlParam, KvOption &option, int &count); + static bool QuerySpecifiedData(const std::string &dbName, const std::string &strSql, + EncrypteAttribute &attribute, int &count); + static bool RepeatCheckAsyncResult(const std::function &inPred, int repeatLimit, + uint32_t repeatInterval); + static bool CompareKey(const DistributedDB::Entry &entry1, const DistributedDB::Entry &entry2); +}; + +// DelegateCallback conclude the Callback implements of function< void(DBStatus, KvStoreSnapshotDelegate*)> +class DelegateCallback { +public: + DelegateCallback() {} + ~DelegateCallback() {} + + // Delete the copy and assign constructors + DelegateCallback(const DelegateCallback &callback) = delete; + DelegateCallback& operator=(const DelegateCallback &callback) = delete; + DelegateCallback(DelegateCallback &&callback) = delete; + DelegateCallback& operator=(DelegateCallback &&callback) = delete; + + void Callback(DistributedDB::DBStatus status, DistributedDB::KvStoreSnapshotDelegate *kvStoreSnapshotDelegate); + + DistributedDB::DBStatus GetStatus(); + + const DistributedDB::KvStoreSnapshotDelegate *GetKvStoreSnapshot() + { + return kvStoreSnapshotDelegate_; + } + +private: + DistributedDB::DBStatus status_ = DistributedDB::DBStatus::INVALID_ARGS; + DistributedDB::KvStoreSnapshotDelegate *kvStoreSnapshotDelegate_ = nullptr; +}; + +// DelegateKvMgrCallback conclude the Callback implements of function< void(DBStatus, KvStoreDelegate*)> +class DelegateKvMgrCallback { +public: + DelegateKvMgrCallback() {} + ~DelegateKvMgrCallback() {} + + // Delete the copy and assign constructors + DelegateKvMgrCallback(const DelegateKvMgrCallback &callback) = delete; + DelegateKvMgrCallback& operator=(const DelegateKvMgrCallback &callback) = delete; + DelegateKvMgrCallback(DelegateKvMgrCallback &&callback) = delete; + DelegateKvMgrCallback& operator=(DelegateKvMgrCallback &&callback) = delete; + + void Callback(DistributedDB::DBStatus status, DistributedDB::KvStoreDelegate *kvStoreDelegate); + + DistributedDB::DBStatus GetStatus(); + + const DistributedDB::KvStoreDelegate *GetKvStore(); + +private: + DistributedDB::DBStatus status_ = DistributedDB::DBStatus::INVALID_ARGS; + DistributedDB::KvStoreDelegate *kvStoreDelegate_ = nullptr; +}; + +std::string TransferStringToHashHexString(const std::string &origStr); + +int RemoveDatabaseDirectory(const std::string &directory); + +class KvStoreObserverImpl final : public DistributedDB::KvStoreObserver { +public: + void OnChange(const DistributedDB::KvStoreChangedData &data); + + KvStoreObserverImpl(); + + ~KvStoreObserverImpl(); + + KvStoreObserverImpl(const KvStoreObserverImpl &); + KvStoreObserverImpl& operator=(const KvStoreObserverImpl &); + + const std::list GetInsertList() const; + + const std::list GetUpdateList() const; + + const std::list GetDeleteList() const; + + int GetChanged() const; + + void WaitUntilReachChangeCount(unsigned int countGoal, uint32_t timeout = 0) const; // timeout in second + // timeout in second + void WaitUntilReachRecordCount(unsigned int countExpect, ListType waitWhat, uint32_t timeout = 0) const; + + microClock_type GetOnChangeTime(); + + void Clear(); + + void SetCumulatedFlag(bool isSaveCumulatedData); + + bool GetCumulatedFlag() const; + + const std::list GetCumulatedInsertList() const; + + const std::list GetCumulatedUpdateList() const; + + const std::list GetCumulatedDeleteList() const; + +private: + std::list insertedEntries_ = {}; + std::list updatedEntries_ = {}; + std::list deleteEntries_ = {}; + unsigned int changed_ = 0; + microClock_type onChangeTime_ + = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + bool isSaveCumulatedData_ = false; + std::list cumulatedInsertList_ = {}; + std::list cumulatedUpdateList_ = {}; + std::list cumulatedDeleteList_ = {}; + // For waiting method + mutable std::mutex waitChangeMutex_; + mutable std::condition_variable waitChangeCv_; +}; + +bool VerifyObserverResult(const KvStoreObserverImpl &pObserver, + int changedTimes, ListType type, const std::list &lst); +bool VerifyObserverResult(const KvStoreObserverImpl &pObserver, + int changedTimes, ListType type, const std::vector &vec); + +class KvStoreSnapshotCallback { +public: + KvStoreSnapshotCallback() {} + ~KvStoreSnapshotCallback() {} + /** + * @tc.steps: step1. Delete the copy and assign constructors. + * @tc.expected: step1. operate successfully. + */ + KvStoreSnapshotCallback(const KvStoreSnapshotCallback &callback) = delete; + KvStoreSnapshotCallback& operator=(const KvStoreSnapshotCallback &callback) = delete; + KvStoreSnapshotCallback(KvStoreSnapshotCallback &&callback) = delete; + KvStoreSnapshotCallback& operator=(KvStoreSnapshotCallback &&callback) = delete; + + void Callback(DistributedDB::DBStatus status, const std::vector &entriesVec); + DistributedDB::DBStatus GetStatus(); + std::vector GetEntries(); + +private: + DistributedDB::DBStatus status_ = DistributedDB::DBStatus::INVALID_ARGS; + std::vector entriesVec_ = {}; +}; + +class AutoLaunchCallback { +public: + AutoLaunchCallback() {} + ~AutoLaunchCallback() {} + + // Delete the copy and assign constructors + AutoLaunchCallback(const AutoLaunchCallback &callback) = delete; + AutoLaunchCallback &operator=(const AutoLaunchCallback &callback) = delete; + AutoLaunchCallback(AutoLaunchCallback &&callback) = delete; + AutoLaunchCallback &operator=(AutoLaunchCallback &&callback) = delete; + + void AutoLaunchNotifier(const std::string &userId, const std::string &appId, const std::string &storeId, + DistributedDB::AutoLaunchStatus status); + bool AutoLaunchRequestNotifier(const std::string &identifier, DistributedDB::AutoLaunchParam ¶m); + int GetStatus(); + void Clear(); + void AddHashIdentity(const std::string &hashIdentity); + void ClearHashIdentities(); + void SetAutoLaunchParam(DistributedDB::AutoLaunchParam &autoLaunchParam); + +private: + int realStatus_ = 0; + std::vector hashIdentities_; + DistributedDB::AutoLaunchParam autoLaunchParam_; +}; +#endif // DISTRIBUTED_DB_MODULE_TEST_TOOLS_H diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributeddb_data_generator.h b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_data_generator.h new file mode 100755 index 000000000..d034dd449 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_data_generator.h @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_MODULE_TEST_TYPES_H +#define DISTRIBUTED_DB_MODULE_TEST_TYPES_H + +#include +#include +#include +#include "types.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_sysinfo.h" +#include "distributeddb_log_print.h" + +struct EntrySize { + unsigned int keySize = 0; + unsigned int valSize = 0; + void Set(unsigned int keySizeInput, unsigned int valSizeInput) + { + this->keySize = keySizeInput; + this->valSize = valSizeInput; + } +}; + +enum DBType { + UNENCRYPTED_DISK_DB = 0, + ENCRYPTED_DISK_DB, + MEMORY_DB +}; + +struct TimeSamp { + std::vector startTime; + std::vector endTime; + TimeSamp() + { + } +}; + +struct ImagePerSamp { + std::vector firstStep; + std::vector secondStep; + ImagePerSamp() + { + } +}; + +struct PerTimeLength { + std::vector entriesDuration; + std::vector cursorDuration; + std::vector moveNextDuration; + std::vector entryDurationForward; + std::vector movePreviousDuration; + std::vector entryDurationBackup; +}; + +struct Schema { + std::vector version; + std::vector mode; + std::vector define; + std::vector index; +}; +struct LongDefine { + int recordNum; + int recordSize; + char prefix; +}; + +enum class RandType { + ALPHA_NUM, + ALPHA_NUM_UNDERLINE, + SPECIAL, +}; + +// ************************ number class ********************************** +const unsigned int OBSERVER_NUM = 8; +const int BUFFER_COUNT = 5; +const unsigned int NB_OBSERVER_NUM = 4; +const unsigned int NB_OPERATION_NUM = 100; +const unsigned int NB_PREDATA_NUM = 50; +const unsigned int RECORDS_SMALL_CNT = 2; +const unsigned int NO_RECORD = 0; +const unsigned int THREE_RECORDS = 3; +const unsigned int THREE_PERF_DATA = 3; +const unsigned int BATCH_RECORDS = 128; +const static int ONE_RECORD = 1; +const static int FOUR_RECORDS = 4; +const static int FIVE_RECORDS = 5; +const static int TEN_RECORDS = 10; +const static int TWENTY_RECORDS = 20; +const static int THIRTYTWO_RECORDS = 32; +const static int FORTY_RECORDS = 40; +const static int FIFTY_RECORDS = 50; +const static int SIXTY_RECORDS = 60; +const static int EIGHTY_RECORDS = 80; +const static int ONE_HUNDRED_RECORDS = 100; +const static int ONE_HUNDRED_AND_TWENTY_RECORDS = 120; +const static int TWO_HUNDREDS_RECORDS = 200; +const static int FIVE_HUNDREDS_RECORDS = 500; +const static int SIX_HUNDREDS_RECORDS = 600; +const static int ONE_THOUSAND_RECORDS = 1000; +const static int TWO_THOUSANDS_RECORDS = 2000; +const static int TWO_FIVE_ZERO_ZERO_RECORDS = 2500; +const static int TWO_FIVE_SIX_ZERO_RECORDS = 2560; +const static int FIVE_THOUSANDS_RECORDS = 5000; +const static int TEN_THOUSAND_RECORDS = 10000; +const static int TWENTY_THOUSAND_RECORDS = 20000; +const static int HUNDRED_THOUSAND_RECORDS = 100000; +const static int FOUR_HUNDRED_THOUSAND_RECORDS = 400000; +const static int FIVE_HUNDRED_THOUSAND_RECORDS = 500000; +const static int TWO_FIVE_SIX_RECORDS = 256; +const static int FIRST_RECORD = 1; +const static int SECOND_RECORD = 2; +const static int FOURTH_RECORD = 4; +const static int FORTIETH_RECORD = 40; +const static int EIGHTIETH_RECORD = 80; +const static int THE_HUNDRED_AND_TWENTY_RECORD = 120; +const static int DATAS_ACCOUNT = 300; +const static int DATA_LEN = 13; +const static int NINE_CNT = 9; +const static unsigned int TWO_DEVICES = 2; +const static unsigned int FOUR_DEVICES = 4; +const static int THIRD_FUNC = 3; +const static int FOURTH_FUNC = 4; +const static int FIFTH_FUNC = 5; +const static int THREE_DBS = 3; +const static int EIGHT_DBS = 8; +const static int TEN_DBS = 10; +const static int ELEVEN_DBS = 11; +const static int FIRST_DB = 1; +const static int FIFTH_DB = 5; +const static int TENTH_DB = 10; +const static int ELEVENTH_DB = 11; + +// ************************ loop times class ************************** +const unsigned int MOD_NUM = 2; +const unsigned int BIG_MOD_NUM = 3; + +const unsigned int ID_CNT_START = 0; +const unsigned int ID_MIN_CNT = 2; +const unsigned int ID_MEDIUM_CNT = 3; +const unsigned int ID_MAX_CNT = 16; +const unsigned int STORE_CNT = 4; +const unsigned int DIR_CNT_START = 0; +const unsigned int DIR_MAX_CNT = 14; +const static int MANYTINES = 3; +const static int ONE_TIME = 1; +const static int TWO_TIMES = 2; +const static int FIVE_TIMES = 5; +const static int FIFTY_TIMES = 50; +const static int HUNDRED_TIMES = 100; + +// ************************ time class ************************** +const unsigned int UNIQUE_SECOND = 1; +const unsigned int TWO_SECONDS = 2; +const unsigned int THREE_SECONDS = 3; +const unsigned int FOUR_SECONDS = 4; +const unsigned int FIVE_SECONDS = 5; +const unsigned int TEN_SECONDS = 10; +const unsigned int FIFTEEN_SECONDS = 15; +const unsigned int TWENTY_SECONDS = 20; +const unsigned int THIRTY_SECONDS = 30; +const unsigned int FORTY_SECONDS = 40; +const unsigned int SIXTY_SECONDS = 60; +const unsigned int ONE_HUNDRED_SECONDS = 100; +const unsigned int TWO_HUNDREDS_SECONDS = 200; +const unsigned int THREE_HUNDREDS_SECONDS = 300; +const unsigned int FOUR_HUNDREDS_SECONDS = 400; +const unsigned int FIVE_HUNDREDS_SECONDS = 500; +const unsigned int SIX_HUNDREDS_SECONDS = 600; +const unsigned int SEVEN_HUNDREDS_SECONDS = 700; +const unsigned int EIGHT_HUNDREDS_SECONDS = 800; +const unsigned int TEN_HUNDREDS_SECONDS = 1000; +const unsigned int ONE_FIVE_ZERO_ZERO_SECONDS = 1500; +const unsigned int SIXTEEN_HUNDREDS_SECONDS = 1600; +const unsigned int EIGHTEEN_HUNDREDS_SECONDS = 1800; +const unsigned int TWENTY_HUNDREDS_SECONDS = 2000; +const unsigned int THREE_THOUSANDS_SECONDS = 3000; +const unsigned int THREE_SIX_ZERO_ZERO_SECONDS = 3600; +const unsigned int EIGHT_THOUSANDS_SECONDS = 8000; +const unsigned int TEN_THOUSANDS_SECONDS = 10000; +const unsigned int WAIT_FOR_LONG_TIME = 15000; +const unsigned int WAIT_FOR_FIFTY_MS = 50000; +const unsigned int WAIT_FOR_LAST_SYNC = 500000; +const unsigned int WAIT_FOR_TWO_HUNDREDS_MS = 200000; +const int FIFTY_MILI_SECONDS = 50; +const int HUNDRED_MILLI_SECONDS = 100; +const static int MILLSECONDES_PER_SECOND = 1000; + +// ************************ length of key class ************************** +const static int KEY_SIX_BYTE = 6; +const static int KEY_EIGHT_BYTE = 8; +const static int KEY_THIRTYTWO_BYTE = 32; +const static int KEY_SIXTYFOUR_BYTE = 64; +const static int KEY_TWO_FIVE_SIX_BYTE = 256; +const static int KEY_ONE_K_BYTE = 1024; +const static int KEY_ONE_HUNDRED_BYTE = 100; + +// ************************ length of value class ************************** +const unsigned int SMALL_VALUE_SIZE = 2; +const unsigned int ONE_K_LONG_STRING = 1024; // 1K +const unsigned int TWO_K_LONG_STRING = 2048; // 2K +const unsigned int TWO_POINT_FOUR_LONG = 2400; // 2.4K +const unsigned int THREE_K_LONG_STRING = 3072; // 3K +const unsigned int FOUR_K_LONG_STRING = 4096; // 4K +const unsigned int ONE_M_LONG_STRING = 1048576; // 1M +const unsigned int TWO_M_LONG_STRING = 2097152; // 2M +const unsigned int FOUR_M_LONG_STRING = 4194304; // 4M +const unsigned int TEN_M_LONG_STRING = 10485760; // 10M +const static int VALUE_ONE_HUNDRED_BYTE = 100; +const static int VALUE_FIVE_HUNDRED_BYTE = 500; +const static int VALUE_ONE_K_BYTE = 1024; +const static int VALUE_HUNDRED_K_BYTE = 102400; +const static int VALUE_TWENTY_K_BYTE = 20480; +const static int VALUE_TWO_POINT_FOUR_K_BYTE = 2458; +const static int PASSWD_BYTE = 129; + +// ************************ bool class ************************************* +const bool IS_LOCAL_ONLY = true; +const bool IS_NOT_LOCAL_ONLY = false; +const bool IS_NEED_CREATE = true; +const bool IS_NOT_NEED_CREATE = false; + +// ************************ range verify class ***************************** +const static int CHANGED_ZERO_TIME = 0; +const static int CHANGED_ONE_TIME = 1; +const static int CHANGED_TWO_TIMES = 2; +const static int CHANGED_THREE_TIMES = 3; +const static int CHANGED_ONE_HUNDRED_TIMES = 100; +const static int CHANGED_TWO_HUNDRED_TIMES = 200; +const static int CHANGED_TWO_FIVE_SIX_TIMES = 256; +const static int CHANGED_ONE_THOUSAND_TIMES = 1000; +const static int CHANGED_TEN_THOUSAND_TIMES = 10000; +const unsigned int RECORDS_NUM_START = 1; +const unsigned int RECORDS_NUM_END = 128; +const unsigned int DEFAULT_START = 1; +const unsigned int DEFAULT_ANOTHER_START = 11; +const unsigned int OBSERVER_CNT_START = 0; +const unsigned int OBSERVER_CNT_END = 8; +const unsigned int NB_OBSERVER_CNT_START = 0; +const unsigned int NB_OBSERVER_CNT_END = 4; +const unsigned int NB_OPERATION_CNT_START = 0; +const unsigned int NB_OPERATION_CNT_END = 5; +const int RAND_BOOL_MIN = 0; +const int RAND_BOOL_MAX = 1; +const int LOCAL_OPER_CNT = 2; +const int NATIVE_OPER_CNT = 6; +const static int OPER_CNT_START = 0; +const static int OPER_CNT_END = 10; +const static int MODE_RAND_MIN = 0; +const static int MODE_RAND_MAX = 2; + +// ************************ USED FOR THREAD ******************************** +const unsigned int SINGLE_THREAD_NUM = 2; +const unsigned int CHAR_SPAN_MIN = 0; +const unsigned int CHAR_SPAN_MAX = 255; + +// ************************ USED FOR INDEX ********************************* +const int INDEX_ZEROTH = 0; +const int INDEX_FIRST = 1; +const int INDEX_SECOND = 2; +const int INDEX_THIRD = 3; +const int INDEX_FORTH = 4; +const int INDEX_FIFTH = 5; +const int INDEX_SIXTH = 6; +const int INDEX_SEVENTH = 7; +const int INDEX_EIGHTTH = 8; +const int INDEX_NINTH = 9; +const int INDEX_NINE_NINE_NINTH = 999; + +// ************************ OTHER CLASS ************************************ +const static int ROUND_BACK = 2; +const static int TRUNC_EIGHT = 100000000; + +const unsigned int PIPE_BUFFER = 128; + +const uint8_t ACSIIEND = 255; + +const int TABLE_MAX = 256; + +const static int ENCRYPT_COUNT = 100; + +const static int EVEN_NUMBER = 2; + +const int VALUE_FIVE_HUNDRED = 500; +const int VALUE_SUM = VALUE_FIVE_HUNDRED + VALUE_FIVE_HUNDRED; +const int VALUE_CHANGE1_FIRST = 400; +const int VALUE_CHANGE1_SECOND = VALUE_SUM - VALUE_CHANGE1_FIRST; +const int VALUE_CHANGE2_FIRST = 700; +const int VALUE_CHANGE2_SECOND = VALUE_SUM - VALUE_CHANGE2_FIRST; + +const static std::string MULTIDB = "/multi_ver/value_storage.db"; +const static std::string KVMULTIDB = "/multi_ver/multi_ver_data.db"; +const static std::string SYNC_MULTI_VER_QUERY_SQL = "select count(*) from version_data;"; +const static std::string DATABASE_INFOR_FILE = "/single_ver/main/gen_natural_store.db"; +const static std::string SYNC_VALUE_SLICE_QUERY_SQL = "select count(*) from data;"; +const static std::string QUERY_SQL = "select count(*) from version_data where key = ?;"; +const static std::string MULTI_KEY_QUERY_SQL = "select count(*) from version_data where key = ? or key = ?;"; + +typedef std::chrono::time_point microClock_type; + +// place some const values for testcases in namespace. +namespace DistributedDBDataGenerator { +const std::string STORE_ID = ""; +const std::string STORE_ID_1 = "STORE_ID_1"; +const std::string STORE_ID_2 = "STORE_ID_2"; +const std::string STORE_ID_3 = "STORE_ID_3"; +const std::string STORE_ID_4 = "STORE_ID_4"; +const std::string STORE_ID_5 = "STORE_ID_5"; +const std::string STORE_ID_6 = "STORE_ID_6"; +const std::string STORE_ID_7 = "STORE_ID_7"; +const std::string STORE_ID_8 = "STORE_ID_8"; +const std::string STORE_ID_9 = "STORE_ID_9"; +const std::string STORE_ID_10 = "STORE_ID_10"; +const std::string SCHEMA_STORE_ID_11 = "SCHEMA_STORE_ID_11"; +const std::string STORE_ID_PERFORM = "STORE_ID_PERFORM"; +const static std::string STORE_ID_SYNC_1 = "SYNC1"; +const static std::string STORE_ID_SYNC_2 = "SYNC2"; +const static std::string STORE_ID_SYNC_3 = "SYNC3"; +const static std::string STORE_ID_SYNC_4 = "SYNC4"; +const static std::string STORE_ID_SYNC_5 = "SYNC5"; +const static std::string STORE_ID_SYNC_6 = "SYNC6"; +const static std::string STORE_ID_SYNC_7 = "SYNC7"; +const static std::string STORE_ID_SYNC_8 = "SYNC8"; +const static std::string STORE_ID_SYNC_9 = "SYNC9"; +const static std::string STORE_ID_SYNC_10 = "SYNC10"; + +const std::string APP_ID = "APP_ID"; +const std::string APP_ID_1 = "APP_ID_1"; +const std::string APP_ID_2 = "APP_ID_2"; +const std::string APP_ID_3 = "APP_ID_3"; +const std::string APP_ID_4 = "APP_ID_4"; +const std::string APP_ID_5 = "APP_ID_5"; +const std::string APP_ID_6 = "APP_ID_6"; +const std::string APP_ID_PERFORM = "APP_ID_PERFORM"; +const std::string APP_ID_NB_1 = "APP_ID_NB_1"; +const std::string APP_ID_NB_2 = "APP_ID_NB_2"; +const std::string APP_ID_LOCAL_1 = "APP_ID_LOCAL_1"; + +const std::string USER_ID = "USER_ID"; +const std::string USER_ID_1 = "USER_ID_1"; +const std::string USER_ID_2 = "USER_ID_2"; +const std::string USER_ID_3 = "USER_ID_3"; +const std::string USER_ID_4 = "USER_ID_4"; +const std::string USER_ID_5 = "USER_ID_5"; +const std::string USER_ID_6 = "USER_ID_6"; +const std::string USER_ID_PERFORM = "USER_ID_PERFORM"; + +const std::vector K1_HASH_KEY = { 0xA2, 0xAB, 0x19, 0x59, 0xC1, 0xC3, 0xBF, 0xA2, 0x95, 0xB0, 0xFC, 0x90, + 0x19, 0x93, 0x78, 0x27, 0x2D, 0xB7, 0x6B, 0x45}; +const std::vector K2_HASH_KEY = { 0xBF, 0xEB, 0x73, 0x4D, 0x2E, 0xB5, 0xD0, 0x91, 0x51, 0x45, 0xC1, 0x86, + 0x12, 0x48, 0x75, 0x7D, 0x4F, 0xD3, 0x2B, 0xC2 }; +const std::vector K3_HASH_KEY = { 0xB5, 0x32, 0xA5, 0x44, 0x0D, 0xD8, 0x42, 0x2D, 0x9D, 0x5F, 0x8D, 0x99, + 0x9B, 0x31, 0x06, 0x87, 0xD4, 0xA2, 0xFE, 0xD9 }; +const std::vector K4_HASH_KEY = { 0x5E, 0xF8, 0x76, 0x6D, 0xE9, 0x35, 0x32, 0x44, 0x24, 0xB5, 0x63, 0xAA, + 0x3E, 0xB0, 0xC7, 0x46, 0x6B, 0x29, 0x3C, 0x94 }; +const std::vector NULL_HASH_KEY = {}; +const DistributedDB::Key KEY_1 = { 'k', '1' }; +const DistributedDB::Key KEY_2 = { 'k', '2' }; +const DistributedDB::Key KEY_3 = { 'k', '3' }; +const DistributedDB::Key KEY_4 = { 'k', '4' }; +const DistributedDB::Key KEY_5 = { 'k', '5' }; +const DistributedDB::Key KEY_6 = { 'k', '6' }; +const DistributedDB::Key KEY_7 = { 'k', '7' }; +const DistributedDB::Key KEY_8 = { 'k', '8' }; +const DistributedDB::Key KEY_9 = { 'k', '9' }; +const DistributedDB::Key KEY_10 = { 'k', '1', '0' }; +const DistributedDB::Key KEY_6_BYTE = { 'k', 'k', 'k', 'k', 'k', '1' }; +const DistributedDB::Key KEY_A_1 = { 'a', 'b', 'c' }; +const DistributedDB::Key KEY_A_2 = { 'a', 'b', 'c', 'd', 'a', 's', 'd' }; +const DistributedDB::Key KEY_A_3 = { 'a', 'b', 'c', 'd', 's' }; +const DistributedDB::Key KEY_BIG_1 = { 'b', 'i', 'g', '1' }; + +const std::vector NULL_K1 = {}; +const DistributedDB::Key KEY_EMPTY = { }; +const DistributedDB::Key KEY_K = { 'k' }; +const DistributedDB::Key KEY_A = { 'a' }; +const DistributedDB::Key OK_KEY_1 = { 'o', 'k' }; + +const DistributedDB::Value VALUE_1 = { 'v', '1' }; +const DistributedDB::Value VALUE_2 = { 'v', '2' }; +const DistributedDB::Value VALUE_3 = { 'v', '3' }; +const DistributedDB::Value VALUE_4 = { 'v', '4' }; +const DistributedDB::Value VALUE_5 = { 'v', '5' }; +const DistributedDB::Value VALUE_6 = { 'v', '6' }; +const DistributedDB::Value VALUE_7 = { 'v', '7' }; +const DistributedDB::Value VALUE_8 = { 'v', '8' }; +const DistributedDB::Value VALUE_9 = { 'v', '9' }; +const DistributedDB::Value VALUE_10 = { 'v', '1', '0' }; +const DistributedDB::Value VALUE_A_1 = { 'a', '1' }; +const DistributedDB::Value VALUE_A_2 = { 'a', '2' }; +const DistributedDB::Value VALUE_A_3 = { 'a', '3' }; +const DistributedDB::Value VALUE_EMPTY = { }; +const DistributedDB::Value OK_VALUE_1 = { 'o', 'k' }; + +const std::vector IMAGE_KEY_PRE = {'a', 'l', 'b', 'u', 'm', '_'}; +const std::vector IMAGE_FILE_KEY_PRE = {'f', 'i', 'l', 'e', '_'}; +const std::string IMAGE_VALUE_PRE = {"\"_id\":23,\"local_media_id\":0," \ + "\"_data\":\"/storage/emulated/0/Pictures/.Gallery2/recycle/GF6DA7BR\"," \ + "\"_size\":427460,\"date_added\":1518606965,\"date_modified\":1519460678," \ + "\"mime_type\":\"image/jpeg\",\"title\":\"MagazinePic-05-2.3.001-bigpicture_05_4\"," \ + "\"description\":\"\",\"_display_name\":\"MagazinePic-05-2.3.001-bigpicture_05_4.jpg\"," \ + "\"orientation\":0,\"latitude\":0,\"longitude\":0,\"datetaken\":1514792938000," \ + "\"bucket_id\":771419238,\"bucket_display_name\":\"MagazineUnlock\",\"duration\":0," \ + "\"resolution\":\"1440x2560\",\"media_type\":1,\"storage_id\":65537,\"width\":1440," \ + "\"height\":2560,\"is_hdr\":0,\"is_hw_privacy\":0,\"hw_voice_offset\":0,\"is_hw_favorite\":0," \ + "\"hw_image_refocus\":0,\"is_hw_burst\":0,\"hw_rectify_offset\":0,\"contenturi\":\"\"," \ + "\"hash\":\"e46cf1bb4773421fbded2e2583fe7130\",\"special_file_list\":0,\"special_file_type\":0," \ + "\"special_file_offset\":0,\"relative_bucket_id\":1793967153,\"albumId\":\"default-album-3\",\"fileType\":1," \ + "\"fileId\":0,\"videoThumbId\":0,\"thumbId\":0,\"lcdThumbId\":0,\"thumbType\":3,\"localThumbPath\":\"\"," \ + "\"localBigThumbPath\":\"\",\"expand\":\"\",\"showDateToken\":1514792938000,\"visit_time\":0," \ + "\"last_update_time\":1519461225861,\"source\":\"\",\"geo_code\":0,\"location_key\":104473884060," \ + "\"story_id\":0,\"story_cluster_state\":\"todo\",\"search_data_status\":0,\"category_id\":-2," \ + "\"portrait_id\":0,\"portrait_cluster_state\":\"todo\",\"dirty\":0,\"recycleFlag\":2," \ + "\"recycledTime\":1519550100614,\"sourcePath\":\"/storage/emulated/0/MagazineUnlock/MagazinePic\"," \ + "\"sourceFileName\":\"MagazinePic-05-2.3.001-bigpict\",\"garbage\":0,\"uniqueId\":0,\"localKey\":\"\"," \ + "\"picture_score\":99,\"cam_perception\":\"\",\"cam_exif_flag\":1,\"sync_status\":0," \ + "\"album_name\":\".MagazineUnlock\",\"ocr_status\":0"}; + +const DistributedDB::Key KEY_SEARCH_0 = KEY_A; +const DistributedDB::Key KEY_SEARCH = { 'a', 'b' }; +const DistributedDB::Key KEY_SEARCH_2 = { 'a', 'b', 'c', 'd', 'e' }; +const std::vector K_SEARCH_3 = { 'b', 'i', 'g'}; +const DistributedDB::Key KEY_SEARCH_3 = { 'b', 'i', 'g'}; +const DistributedDB::Key KEY_SEARCH_4 = { 'k' }; +const std::vector K_SEARCH_5 = { 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k' }; +const DistributedDB::Key KEY_SEARCH_6 = { 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k', 'k', '6' }; +const DistributedDB::Key PERFORMANCEKEY = { 'p', 'e', 'r' }; +const DistributedDB::Key KEY_CONS_1 = { 'c', 'o', 'n', 's', '1' }; +const DistributedDB::Key KEY_CONS_2 = { 'c', 'o', 'n', 's', '2' }; +const DistributedDB::Key KEY_BATCH_CONS_1 = { 'r', 'e', 's', 'u', 'l', 't', '1' }; +const DistributedDB::Key KEY_BATCH_CONS_2 = { 'r', 'e', 's', 'u', 'l', 't', '2' }; +const DistributedDB::Key KEY_BATCH_CONS_3 = { 'r', 'e', 's', 'u', 'l', 't', '3' }; +const std::vector KEYS_1 = { KEY_1, KEY_2 }; +const DistributedDB::Entry ENTRY_1 = { .key = KEY_1, .value = VALUE_1 }; +const DistributedDB::Entry ENTRY_2 = { .key = KEY_2, .value = VALUE_2 }; +const DistributedDB::Entry ENTRY_3 = { .key = KEY_3, .value = VALUE_3 }; +const DistributedDB::Entry ENTRY_4 = { .key = KEY_4, .value = VALUE_4 }; +const DistributedDB::Entry ENTRY_5 = { .key = KEY_5, .value = VALUE_5 }; +const DistributedDB::Entry ENTRY_6 = { .key = KEY_6, .value = VALUE_6 }; +const DistributedDB::Entry ENTRY_7 = { .key = KEY_7, .value = VALUE_7 }; +const DistributedDB::Entry ENTRY_8 = { .key = KEY_8, .value = VALUE_8 }; +const DistributedDB::Entry ENTRY_9 = { .key = KEY_9, .value = VALUE_9 }; +const DistributedDB::Entry ENTRY_10 = { .key = KEY_10, .value = VALUE_10 }; +const DistributedDB::Entry ENTRY_1_2 = { .key = KEY_1, .value = VALUE_2 }; +const DistributedDB::Entry ENTRY_2_3 = { .key = KEY_2, .value = VALUE_3 }; +const DistributedDB::Entry ENTRY_3_1 = { .key = KEY_3, .value = VALUE_1 }; +const DistributedDB::Entry ENTRY_4_5 = { .key = KEY_4, .value = VALUE_5 }; +const DistributedDB::Entry ENTRY_5_6 = { .key = KEY_5, .value = VALUE_6 }; +const DistributedDB::Entry ENTRY_2_1 = { .key = KEY_2, .value = VALUE_1 }; +const DistributedDB::Entry ENTRY_1_3 = { .key = KEY_1, .value = VALUE_3 }; +const DistributedDB::Entry ENTRY_2_4 = { .key = KEY_2, .value = VALUE_4 }; +const DistributedDB::Entry ENTRY_1_4 = { .key = KEY_1, .value = VALUE_4 }; +const DistributedDB::Entry ENTRY_3_4 = { .key = KEY_3, .value = VALUE_4 }; +const DistributedDB::Entry ENTRY_1_NULL = { .key = KEY_1, .value = VALUE_EMPTY }; +const DistributedDB::Entry ENTRY_2_NULL = { .key = KEY_2, .value = VALUE_EMPTY }; +const DistributedDB::Entry ENTRY_3_NULL = { .key = KEY_3, .value = VALUE_EMPTY }; +const DistributedDB::Entry ENTRY_NULL_1 = { .key = KEY_EMPTY, .value = VALUE_2 }; +const DistributedDB::Entry ENTRY_NULL_2 = { .key = KEY_EMPTY, .value = VALUE_3 }; +const DistributedDB::Entry ENTRY_NULL_3 = { .key = KEY_EMPTY, .value = VALUE_1 }; +const DistributedDB::Entry ENTRY_NULL = { .key = KEY_EMPTY, .value = VALUE_EMPTY }; +const DistributedDB::Entry ENTRY_A_1 = { .key = KEY_A_1, .value = VALUE_A_1 }; +const DistributedDB::Entry ENTRY_A_2 = { .key = KEY_A_2, .value = VALUE_A_2 }; +const DistributedDB::Entry ENTRY_A_3 = { .key = KEY_A_3, .value = VALUE_A_3 }; +const DistributedDB::Entry ENTRY_A_1_2 = { .key = KEY_A_1, .value = VALUE_A_2 }; +const DistributedDB::Entry ENTRY_A_2_3 = { .key = KEY_A_2, .value = VALUE_A_3 }; +const DistributedDB::Entry ENTRY_A_3_1 = { .key = KEY_A_3, .value = VALUE_A_1 }; + +const DistributedDB::CipherPassword NULL_PASSWD; +const std::vector NULL_PASSWD_VECTOR = {}; +const std::vector PASSWD_VECTOR_1 = {'P', 'a', 's', 's', 'w', 'o', 'r', 'd', '@', '1', '2', '3'}; +const std::vector PASSWD_VECTOR_2 = {'P', 'a', 's', 's', 'w', 'o', 'r', 'd', '@', 'c', 'o', 'm'}; +const std::vector FILE_PASSWD_VECTOR_1 = {'F', 'i', 'l', 'e', 'p', 'a', 's', 's', 'w', 'd', '1'}; +const std::vector FILE_PASSWD_VECTOR_2 = {'F', 'i', 'l', 'e', 'p', 'a', 's', 's', 'w', 'd', '2'}; + +void GenerateRecord(unsigned int keyNo, DistributedDB::Entry &entry, std::vector keyPrifix = { 'k' }); + +void GenerateCharSet(std::vector &charSet); + +void GenerateAlphaNumUnderlineCharSet(std::vector &charSet); + +void GenerateSpecialCharSet(std::vector &charSet); + +void GenerateFixedLenRandString(unsigned int neededLen, RandType randType, std::string &genString); + +void GenerateRandRecord(DistributedDB::Entry &entry, EntrySize &entrySize, unsigned int keyNo); + +void GenerateRecords(unsigned int recordNum, unsigned int start, + std::vector &allKeys, std::vector &entriesBatch, + std::vector keyPrifix = { 'k' }); +void GenerateMaxBigRecord(unsigned int keyNo, DistributedDB::Entry &entry, + const std::vector &keyPrefix, unsigned int num); +bool GenerateMaxBigRecords(unsigned int recordNum, unsigned int start, + std::vector &allKeys, std::vector &entriesBatch); + +void GenerateTenThousandRecords(unsigned int recordNum, unsigned int start, + std::vector &allKeys, std::vector &entriesBatch); + +void GenerateNormalAsciiRecords(DistributedDB::Entry &entry); + +void GenerateFullAsciiRecords(DistributedDB::Entry &entry); + +void GenerateBiggistKeyRecords(DistributedDB::Entry &entry); + +DistributedDB::Entry GenerateFixedLenKVRecord(unsigned int serialNo, + unsigned int keyLen, uint8_t keyFilledChr, + unsigned int valueLen, uint8_t valueFilledChr); + +void GenerateFixedRecords(std::vector &entries, + std::vector &allKeys, + int recordNum, unsigned int keySize, unsigned int valSize); + +void GenerateOneRecordForImage(int entryNo, const EntrySize &entrySize, + const std::vector &keyPrefix, const std::vector &val, DistributedDB::Entry &entry); +void GenerateRecordsForImage(std::vector &entries, EntrySize &entrySize, + int num, std::vector keyPrefix = {'k'}, std::vector val = {'v'}); + +void GenerateAppointPrefixAndSizeRecord(int recordNo, const EntrySize &entrySize, + const std::vector &keyPrefix, const std::vector &valPrefix, DistributedDB::Entry &entry); +void GenerateAppointPrefixAndSizeRecords(std::vector &entries, const EntrySize &entrySize, + int num, const std::vector &keyPrefix = {'k'}, const std::vector &valPrefix = {'v'}); + +int GetRandInt(const int randMin, const int randMax); + +void GenerateFixedLenRandRecords(std::vector &entries, + std::vector &allKeys, + int recordNum, unsigned int keySize, unsigned int valSize); + +const std::string GetDbType(const int type); +void GenerateRandomRecords(std::vector &entries, EntrySize entrySize, int num); +void GetLongSchemaDefine(LongDefine ¶m, std::string &longDefine); +const std::string SpliceToSchema(const std::string &version, const std::string &mode, + const std::string &define, const std::string &index = "", const std::string &skipSize = ""); +std::vector GetValidSchema(Schema &validSchema, bool hasIndex); +std::map> GetInvalidSchema(Schema &invalidSchema, Schema &validSchema, bool hasIndex); +} // DistributedDBDataGenerator +#endif // DISTRIBUTED_DB_MODULE_TEST_TYPES_H diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributeddb_log_print.h b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_log_print.h new file mode 100755 index 000000000..4ce2bd140 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_log_print.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_LOG_PRINT_H +#define DISTRIBUTED_DB_LOG_PRINT_H + +#if defined USING_PRINTF_LOGGER + #include +#elif defined USING_HILOG_LOGGER + #include "hilog/log.h" +#endif + +#if defined USING_PRINTF_LOGGER + #define MST_LOG(fmt, ...) \ + (void)(std::printf(fmt"\n", ##__VA_ARGS__)) +#elif defined USING_HILOG_LOGGER + static constexpr OHOS::HiviewDFX::HiLogLabel LOG_LABEL = { LOG_CORE, 0xD001630, "DistributedDB[TEST]" }; + #define MST_LOG(fmt, ...) \ + OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#endif +#endif diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributeddb_nb_test_tools.h b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_nb_test_tools.h new file mode 100755 index 000000000..7035be240 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_nb_test_tools.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_NB_TEST_TOOLS_H +#define DISTRIBUTEDDB_NB_TEST_TOOLS_H + +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_observer.h" +#include "query.h" +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" + +const std::string NB_DIRECTOR = "/data/test/nbstub/"; // default work dir. + +struct DBParameters { + std::string storeId; + std::string appId; + std::string userId; + DBParameters(std::string storeIdStr, std::string appIdStr, std::string userIdStr) + : storeId(storeIdStr), appId(appIdStr), userId(userIdStr) + { + } +}; + +struct Option { + bool createIfNecessary = true; + bool isMemoryDb = false; // true represents using a memory database + bool isEncryptedDb = false; // whether need encrypt + DistributedDB::CipherType cipher = DistributedDB::CipherType::DEFAULT; // cipher type + std::vector passwd; // cipher password + std::string schema; + bool createDirByStoreIdOnly = false; + Option(bool createIfNecessary1, bool isMemoryDb1, bool isEncryptedDb1, DistributedDB::CipherType cipher1, + std::vector passwd1) + : createIfNecessary(createIfNecessary1), isMemoryDb(isMemoryDb1), isEncryptedDb(isEncryptedDb1), + cipher(cipher1), passwd(passwd1) + { + } + Option() + {} +}; + +struct ConflictData { + DistributedDB::KvStoreNbConflictType type; + DistributedDB::Key key; + DistributedDB::Value oldValue; + DistributedDB::Value newValue; + bool oldIsDeleted = false; + bool newIsDeleted = false; + bool oldIsNative = false; + bool newIsNative = false; +}; + +enum EntryType { + INSERT_LOCAL = 0, + INSERT_NATIVE = 1, + DELETE_LOCAL = 2, + DELETE_NATIVE = 3, + UPDATE_LOCAL = 4, + UPDATE_NATIVE = 5 +}; + +enum class ReadOrWriteTag { + READ = 0, + WRITE = 1, + DELETE = 2, + REGISTER = 3 +}; + +// default kvStoreDelegateManager's config. +const static DistributedDB::KvStoreConfig CONFIG = { + .dataDir = NB_DIRECTOR +}; + +const static DBParameters g_dbParameter1(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_1); +const static DBParameters g_dbParameter2(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_2); +const static DBParameters g_dbParameter3(DistributedDBDataGenerator::STORE_ID_3, + DistributedDBDataGenerator::APP_ID_3, DistributedDBDataGenerator::USER_ID_3); +const static DBParameters g_dbParameter4(DistributedDBDataGenerator::STORE_ID_4, + DistributedDBDataGenerator::APP_ID_4, DistributedDBDataGenerator::USER_ID_4); +const static DBParameters g_dbParameter5(DistributedDBDataGenerator::STORE_ID_5, + DistributedDBDataGenerator::APP_ID_5, DistributedDBDataGenerator::USER_ID_5); +const static DBParameters g_dbParameter6(DistributedDBDataGenerator::STORE_ID_6, + DistributedDBDataGenerator::APP_ID_6, DistributedDBDataGenerator::USER_ID_6); +const static DBParameters g_dbParameter2_1(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_1); +const static DBParameters DB_PARAMETER_0_1(DistributedDBDataGenerator::STORE_ID, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_1); +const static DBParameters g_dbParameter1_2_1(DistributedDBDataGenerator::STORE_ID_1, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_1); +const static DBParameters g_dbParameter2_1_2(DistributedDBDataGenerator::STORE_ID_2, + DistributedDBDataGenerator::APP_ID_1, DistributedDBDataGenerator::USER_ID_2); +const static DBParameters g_dbParameter3_2_1(DistributedDBDataGenerator::STORE_ID_3, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_1); +const static DBParameters g_dbParameter4_2_2(DistributedDBDataGenerator::STORE_ID_4, + DistributedDBDataGenerator::APP_ID_2, DistributedDBDataGenerator::USER_ID_2); +const static Option g_createDiskUnencrypted(true, false, false, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::NULL_PASSWD_VECTOR); +const static Option g_createDiskEncrypted(true, false, true, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::PASSWD_VECTOR_1); +const static Option g_ncreateDiskUnencrypted(false, false, false, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::NULL_PASSWD_VECTOR); +const static Option g_ncreateDiskEncrypted(false, false, true, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::PASSWD_VECTOR_1); +const static Option g_createMemUnencrypted(true, true, false, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::NULL_PASSWD_VECTOR); +static Option g_option = g_createDiskEncrypted; +// DelegateMgrNbCallback conclude the Callback implements of function< void(DBStatus, KvStoreDelegate*)> +class DelegateMgrNbCallback { +public: + DelegateMgrNbCallback() {} + ~DelegateMgrNbCallback() {} + + // Delete the copy and assign constructors + DelegateMgrNbCallback(const DelegateMgrNbCallback &callback) = delete; + DelegateMgrNbCallback& operator=(const DelegateMgrNbCallback &callback) = delete; + DelegateMgrNbCallback(DelegateMgrNbCallback &&callback) = delete; + DelegateMgrNbCallback& operator=(DelegateMgrNbCallback &&callback) = delete; + + void Callback(DistributedDB::DBStatus status, DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate); + DistributedDB::DBStatus GetStatus(); + DistributedDB::KvStoreNbDelegate *GetKvStore(); + +private: + DistributedDB::DBStatus status_ = DistributedDB::DBStatus::INVALID_ARGS; + DistributedDB::KvStoreNbDelegate *kvStoreNbDelegate_ = nullptr; +}; + +class ConflictNbCallback { +public: + ConflictNbCallback() {} + ~ConflictNbCallback() {} + + // Delete the copy and assign constructors + ConflictNbCallback(const ConflictNbCallback &callback) = delete; + ConflictNbCallback &operator=(const ConflictNbCallback &callback) = delete; + ConflictNbCallback(ConflictNbCallback &&callback) = delete; + ConflictNbCallback &operator=(ConflictNbCallback &&callback) = delete; + + void NotifyCallBack(const DistributedDB::KvStoreNbConflictData &data, std::vector *&conflictData); +}; + +class DistributedDBNbTestTools final { +public: + + DistributedDBNbTestTools() {} + ~DistributedDBNbTestTools() {} + + // Delete the copy and assign constructors + DistributedDBNbTestTools(const DistributedDBNbTestTools &distributedDBNbTestTools) = delete; + DistributedDBNbTestTools& operator=(const DistributedDBNbTestTools &distributedDBNbTestTools) = delete; + DistributedDBNbTestTools(DistributedDBNbTestTools &&distributedDBNbTestTools) = delete; + DistributedDBNbTestTools& operator=(DistributedDBNbTestTools &&distributedDBNbTestTools) = delete; + static DistributedDB::KvStoreNbDelegate* GetNbDelegateStatus(DistributedDB::KvStoreDelegateManager *&outManager, + DistributedDB::DBStatus &statusReturn, const DBParameters ¶m, const Option &optionParam); + static DistributedDB::KvStoreNbDelegate* GetNbDelegateSuccess(DistributedDB::KvStoreDelegateManager *&outManager, + const DBParameters ¶m, const Option &optionParam); + static DistributedDB::DBStatus GetNbDelegateStoresSuccess(DistributedDB::KvStoreDelegateManager *&outManager, + std::vector &outDelegateVec, const std::vector &storeIds, + const std::string &appId, const std::string &userId, const Option &optionParam); + static DistributedDB::KvStoreNbDelegate::Option TransferNbOptionType(const Option &optionParam); + // this static method is to compare if the two Value has the same data. + static bool isValueEquals(const DistributedDB::Value &v1, const DistributedDB::Value &v2); + + static DistributedDB::DBStatus Get(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, DistributedDB::Value &value); + + static DistributedDB::DBStatus GetEntries(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &keyPrefix, std::vector &entries); + + static DistributedDB::DBStatus Put(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, const DistributedDB::Value &value); + + static DistributedDB::DBStatus PutBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &entries); + + static DistributedDB::DBStatus Delete(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key); + + static DistributedDB::DBStatus DeleteBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &keys); + + static DistributedDB::DBStatus GetLocal(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, DistributedDB::Value &value); + + static DistributedDB::DBStatus PutLocal(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, const DistributedDB::Value &value); + + static DistributedDB::DBStatus PutLocalBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &entries); + + static DistributedDB::DBStatus DeleteLocal(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key); + + static DistributedDB::DBStatus DeleteLocalBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &keys); + + static DistributedDB::DBStatus RegisterObserver(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, unsigned int mode, DistributedDB::KvStoreObserver *observer); + + static DistributedDB::DBStatus UnRegisterObserver(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::KvStoreObserver *observer); + static bool CloseNbAndRelease(DistributedDB::KvStoreDelegateManager *&manager, + DistributedDB::KvStoreNbDelegate *&delegate); + static bool ModifyDatabaseFile(const std::string &fileDir); + static std::string GetKvNbStoreDirectory(const DBParameters ¶m); + static bool MoveToNextFromBegin(DistributedDB::KvStoreResultSet &resultSet, + const std::vector &entries, int recordCnt); +}; + +struct CallBackParam { + const std::string path = ""; + bool result = true; +}; + +class KvStoreNbCorruptInfo { +public: + KvStoreNbCorruptInfo() {} + ~KvStoreNbCorruptInfo() {} + + KvStoreNbCorruptInfo(const KvStoreNbCorruptInfo&) = delete; + KvStoreNbCorruptInfo& operator=(const KvStoreNbCorruptInfo&) = delete; + KvStoreNbCorruptInfo(KvStoreNbCorruptInfo&&) = delete; + KvStoreNbCorruptInfo& operator=(KvStoreNbCorruptInfo&&) = 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) + { + MST_LOG("The corrupt Db is %s, %s, %s", appId.c_str(), userId.c_str(), storeId.c_str()); + } + void CorruptNewCallBack(const std::string &appId, const std::string &userId, const std::string &storeId, + DistributedDB::KvStoreDelegateManager *&manager, CallBackParam ¶m); + void CorruptCallBackOfImport(const std::string &appId, const std::string &userId, const std::string &storeId, + DistributedDB::KvStoreNbDelegate *&delegate, CallBackParam ¶m); + void CorruptCallBackOfExport(const std::string &appId, const std::string &userId, const std::string &storeId, + DistributedDB::KvStoreNbDelegate *&delegate, CallBackParam ¶m); +}; + +template +class QueryGenerate { +public: + static constexpr int FP_COMP_SIZE = 6; + static constexpr int FP_LIKE_SIZE = 2; + static constexpr int FP_IN_SIZE = 2; + + typedef DistributedDB::Query&(DistributedDB::Query::*FP_COMP)(const std::string& field, const ParaType& value); + typedef DistributedDB::Query&(DistributedDB::Query::*FP_LIKE)(const std::string& field, const std::string& value); + typedef DistributedDB::Query&(DistributedDB::Query::*FP_IN)(const std::string& field, + const std::vector& value); + + static QueryGenerate& Instance() + { + static QueryGenerate queryGenerateSingleton; + return queryGenerateSingleton; + } + + void GenerateQueryComp(std::vector &queries, const bool (&funcFilter)[FP_COMP_SIZE], + const std::string& field, const ParaType& value) + { + for (int i = 0; i < FP_COMP_SIZE; i++) { + if (!funcFilter[i]) { + continue; + } + queries.push_back((DistributedDB::Query::Select().*(compFunc_[i]))(field, value)); + } + } + + void GenerateQueryLike(std::vector &queries, const bool (&funcFilter)[FP_LIKE_SIZE], + const std::string& field, const std::string& value) + { + for (int i = 0; i < FP_LIKE_SIZE; i++) { + if (!funcFilter[i]) { + continue; + } + queries.push_back((DistributedDB::Query::Select().*(likeFunc_[i]))(field, value)); + } + } + void GenerateQueryIn(std::vector &queries, const bool (&funcFilter)[FP_IN_SIZE], + const std::string& field, const std::vector& value) + { + for (int i = 0; i < FP_IN_SIZE; i++) { + if (!funcFilter[i]) { + continue; + } + queries.push_back((DistributedDB::Query::Select().*(inFunc_[i]))(field, value)); + } + } +private: + std::vector compFunc_{ + &DistributedDB::Query::EqualTo, + &DistributedDB::Query::NotEqualTo, + &DistributedDB::Query::GreaterThan, + &DistributedDB::Query::GreaterThanOrEqualTo, + &DistributedDB::Query::LessThan, + &DistributedDB::Query::LessThanOrEqualTo, + }; + std::vector likeFunc_{ + &DistributedDB::Query::Like, + &DistributedDB::Query::NotLike, + }; + std::vector inFunc_{ + &DistributedDB::Query::In, + &DistributedDB::Query::NotIn, + }; +}; + +bool EndCaseDeleteDB(DistributedDB::KvStoreDelegateManager *&manager, DistributedDB::KvStoreNbDelegate *&nbDelegate, + const std::string base, bool isMemoryDb); + +#endif // DISTRIBUTEDDB_NB_TEST_TOOLS_H \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/include/distributeddb_schema_test_tools.h b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_schema_test_tools.h new file mode 100755 index 000000000..7faa44956 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/include/distributeddb_schema_test_tools.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 DISTRIBUTED_DB_SCHEMA_TEST_TOOLS_H +#define DISTRIBUTED_DB_SCHEMA_TEST_TOOLS_H + +#ifdef RUNNING_ON_SIMULATED_ENV +#include +#endif +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" +#include "distributeddb_data_generator.h" +const static int RECORDNUM = 257; +const static int TWO_RECORD = 2; +const static int RECORDSIZE = 4; +const static int ARRAY_SIZE = 3; +const static int OVER_MAXSIZE = 4097; +const static int SCHEMA_INDEX = 5; +const static unsigned int INDEX_FIFY = 5; +const static unsigned int INDEX_SIX = 6; +const static unsigned int INDEX_NINE = 9; +const static unsigned int INDEX_TEN = 10; +// the valid std::string of schema field +const static std::string VALID_VERSION_1 = "1.0"; +const std::vector VALID_VERSION = {VALID_VERSION_1}; +const static std::string VALID_MODE_1 = "STRICT"; +const static std::string VALID_MODE_2 = "COMPATIBLE"; +const std::vector VALID_MODE = {VALID_MODE_1, VALID_MODE_2}; +const static std::string VALID_DEFINE_1 = "{\"field1\":\"STRING,NOT NULL,DEFAULT 'fxy'\"," \ + "\"_field1\":{\"field1\":\"STRING,NOT NULL,DEFAULT 'fxy'\",\"field2\":\"STRING ,DEFAULT null\"," \ + "\"field3\":{\"field4\":\"STRING,NOT NULL \",\"field5\":{\"field6\":\"STRING\t\"," \ + "\"field7\":[]}}},\"field1\":\"BOOL,NOT NULL,DEFAULT true\"," \ + "\"field9\":\"BOOL,DEFAULT false\",\"Field9\":\"\tBOOL\"," \ + "\"field10\":{\"field10\":\"BOOL,DEFAULT null\",\"field11\":\"INTEGER, NOT NULL,DEFAULT -10\"," \ + "\"field12\":\"INTEGER,DEFAULT null\",\"field13\":\"INTEGER \",\"field14\":{}}," \ + "\"field15\":\"LONG,NOT NULL,DEFAULT +10\",\"field16\":\"LONG,DEFAULT null\",\"field17\":\" LONG\"," \ + "\"field18\":\"DOUBLE,NOT NULL,DEFAULT -0.0\",\"_19\":\"DOUBLE,DEFAULT null\",\"f\":\"DOUBLE\"}"; +const std::vector VALID_DEFINE = {VALID_DEFINE_1}; +const static std::string VALID_INDEX_1 = "[]"; +const static std::string VALID_INDEX_2 = "[\" $.field1\",\"$.field10.field10 \",\"$.field10.field11\r\",\"$.f\"]"; +const static std::string VALID_INDEX_3 = "[\"\t$.field1\",\" $._field1.field1\",\"$._field1.field3.field5.field6 \"," \ + "\"$.field9\",\"$.Field9\"]"; +const static std::string VALID_INDEX_4 = "[\"$.field9\",\"$.field10.field10\",\"$._field1.field3.field4\"," \ + "\"$._field1.field3.field5.field6\"]"; +const std::vector VALID_INDEX = {VALID_INDEX_1, VALID_INDEX_2, VALID_INDEX_3}; +const static std::string INVALID_INDEX_1 = "[19]"; +const static std::string INVALID_INDEX_2 = "[\"$.field1\",\"$._field1.field5.field6\",\"$.field10.field13\"," \ + "\"$field20\"]"; +const static std::string INVALID_INDEX_3 = "[\"$.field1\",\"$._field1.field5.field6\",\"$.field11.field10\"]"; +const static std::string INVALID_INDEX_4 = "[\".field1\",\"$.field10.field11\"]"; +const static std::string INVALID_INDEX_5 = "[\"$$.field1\",\"$.field10.field11\"]"; +const static std::string INVALID_INDEX_6 = "[\"$.field1\",\"$.field10 .field11\"]"; +const static std::string INVALID_INDEX_7 = "[\"$.field1\",\"$ .field10.field11\"]"; +const static std::string INVALID_INDEX_8 = "[\"$.field1\",\"$.field1\"]"; +const static std::string INVALID_INDEX_9 = "[\"$.field10.field14\"]"; +const std::vector INALID_INDEX = {INVALID_INDEX_1, INVALID_INDEX_2, INVALID_INDEX_3, INVALID_INDEX_4, + INVALID_INDEX_5, INVALID_INDEX_6, INVALID_INDEX_7, INVALID_INDEX_8, INVALID_INDEX_9}; +const static std::string INVALID_VERSION_1 = ""; +const static std::string INVALID_VERSION_2 = "1"; +const static std::string INVALID_VERSION_3 = "1.1"; +const std::vector INVALID_VERSION = {INVALID_VERSION_1, INVALID_VERSION_2, INVALID_VERSION_3}; +const static std::string INVALID_MODE_1 = "strict"; +const static std::string INVALID_MODE_2 = "compatible"; +const static std::string INVALID_MODE_3 = "LOOSE"; +const static std::string INVALID_MODE_4 = ""; +const std::vector INVALID_MODE = {INVALID_MODE_1, INVALID_MODE_2, INVALID_MODE_3, INVALID_MODE_4}; +const static std::string INVALID_DEFINE_1 = "{}"; +const static std::string INVALID_DEFINE_2 = "{\"field1\":{\"field2\":{\"field3\":{\"field4\":{\"field5\":{}}}}}}"; +static std::vector VALID_DEFINE_FIELD = {"\"field\""}; +static std::vector INVALID_DEFINE_FIELD = {"\"field!\"", "\"_ \"", "\"1_field\"", "field", "\"\""}; +static std::vector VALID_TYPE = {"STRING", "BOOL", "INTEGER", "LONG", "DOUBLE"}; +static std::vector INVALID_TYPE = {"", "string", "FLOAT"}; +static std::vector VALID_NOTNULL = {"NOT NULL"}; +static std::vector INVALID_NOTNULL = {"", "NULL", "null", "\"null\"", "EMPTY", "NOTNULL", + "NOT NULL", "NOT_NULL"}; +static std::vector VALID_DEFAULT = {"DEFAULT 'fxy'", "DEFAULT null", ""}; +static std::vector INVALID_DEFAULT = {"DAFAULT", "default", "DEFAULTtrue", "DEFAULT null", + "DEFAULT \"fxy\"", "DEFAULT 'true'", "DEFAULT 'false'", "DAFAULT TRUE", "DEFAULT FALSE", "DEFAULT '10'", + "DEFAULT '10.5'", "DEFAULT 10e4", "DEFAULT 1.05E-5", "DEFAULT 0X1A", ""}; +// the order of three attributes is invalid +const std::string ATTRIBUTES_PRE1 = "\"INTEGER,NOT NULL,DEFAULT "; +const std::string ATTRIBUTES_PRE2 = "\"LONG,NOT NULL,DEFAULT "; +const std::string ATTRIBUTES_PRE3 = "\"DOUBLE,NOT NULL,DEFAULT "; +static std::vector INVALID_ATTRIBUTES = { + "\"STRING,DEFAULT 'fxy',NOT NULL\"", + "\"DEFAULT 'fxy',NOT NULL,STRING\"", + "\"DEFAULT 'fxy',STRING,NOT NULL\"", + "\"NOT NULL,DEFAULT 'fxy',STRING\"", + "\"NOT NULL,STRING,DEFAULT 'fxy'\"", + "\"STRING,\"", + "\"STRING,DEFAULT\"", + "\"NOT NULL,DAFAULT 'fxy'\"", + "\"DEFAULT 'fxy'\"", + "\"NOT NULL\"", + "\"STRING,DEFAULT NULL\"", + "\"INTEGER,DEFAULT 'null'\"", + "\"STRING,NOT NULL,DEFAULT 'fxy',\"", + ATTRIBUTES_PRE1 + std::to_string(INT32_MAX) + "1\"", + ATTRIBUTES_PRE1 + std::to_string(INT32_MIN) + "1\"", + ATTRIBUTES_PRE2 + std::to_string(INT64_MAX) + "1\"", + ATTRIBUTES_PRE2 + std::to_string(INT64_MIN) + "1\"", + ATTRIBUTES_PRE3 + "1" + std::to_string(DBL_MAX) + "\"", + ATTRIBUTES_PRE3 + "-1" + std::to_string(DBL_MAX) + "\"" +}; +struct SchemaDefine { + std::vector field; + std::vector type; + std::vector notnull; + std::vector defaultValue; +}; + +static std::string VALUE_MATCH_1 = "\"_field1\":{\"field1\":\"abc\",\"field2\":null,\"field3\":{\"field4\":\"def\"," \ + "\"field5\":{\"field6\":\"fxy\",\"field7\":[]}}}"; +static std::string VALUE_MATCH_2 = "\"field1\":false,\"field9\":null,\"Field9\":true,\"field10\":{\"field10\":true," \ + "\"field11\":-1000000,\"field12\":null,\"field13\":150000,\"field14\":{}},\"field15\":666,\"field16\":null," \ + "\"field17\":-100,\"field18\":-1.05e-4,\"_19\":0.0,\"f\":null"; + +const std::string VALID_COMBINATION_DEFINE = "{\"field1\":\"STRING ,DEFAULT null\",\"field2\":" \ + "{\"field3\":\"BOOL ,DEFAULT null\",\"field4\":{\"field5\":\"INTEGER ,DEFAULT null\",\"field6\":" \ + "{\"field7\":\"LONG ,DEFAULT null\",\"field8\":\"DOUBLE ,DEFAULT null\"}}}}"; + +const static std::string PERF_SCHEMA_DEFINE = "{\"field1\":\"LONG\"," \ + "\"field2\":\"STRING\", \"field3\":\"STRING\", \"field4\":\"STRING\", \"field5\":\"STRING\"," \ + "\"field6\":\"DOUBLE\", \"field7\":\"STRING\", \"field8\":\"STRING\", \"field9\":\"STRING\"," \ + "\"field10\":\"STRING\", \"field11\":\"STRING\", \"field12\":\"STRING\", \"field13\":\"STRING\"," \ + "\"field14\":\"STRING\", \"field15\":\"STRING\", \"field16\":\"STRING\", \"field17\":\"STRING\"," \ + "\"field18\":\"STRING\", \"field19\":\"STRING\", \"field20\":\"STRING\", \"field21\":\"STRING\"," \ + "\"field22\":\"STRING\", \"field23\":\"STRING\", \"field24\":\"STRING\", \"field25\":\"STRING\"," \ + "\"field26\":\"STRING\", \"field27\":\"STRING\", \"field28\":\"STRING\", \"field29\":\"STRING\"," \ + "\"field30\":\"STRING\"}"; +const static std::string PERF_SCHEMA_SIX_INDEXES = "[\"$.field1\",\"$.field2\",\"$.field3\",\"$.field4\", " \ + "\"$.field5\", \"$.field6\"]"; +const static std::string SKIP_SIZE = "1"; +const static unsigned int FIRST_FIELD = 1; +const static unsigned int SECOND_FIELD = 2; +const static unsigned int THIRD_FIELD = 3; +const static unsigned int FOURTH_FIELD = 4; +const static unsigned int FIFTH_FIELD = 5; +const static unsigned int SIXTH_FIELD = 6; +const static unsigned int THIRTIETH_FIELD = 30; +const static unsigned int TEST_START_CNT = 1; + +const static std::string VALUE_SKIP_STRING = "a"; +struct RecordInfo { + uint8_t keyFilledChr; + unsigned int keyLength; + uint8_t valueFilledChr; + unsigned int valueLength; + RecordInfo(uint8_t keyFilledChr, unsigned int keyLength, uint8_t valueFilledChr, unsigned int valueLength) + : keyFilledChr(keyFilledChr), keyLength(keyLength), valueFilledChr(valueFilledChr), valueLength(valueLength) + { + } +}; +class DistributedDBSchemaTestTools final { +public: + + DistributedDBSchemaTestTools() {} + ~DistributedDBSchemaTestTools() {} + + // Delete the copy and assign constructors + DistributedDBSchemaTestTools(const DistributedDBSchemaTestTools &DistributedDBSchemaTestTools) = delete; + DistributedDBSchemaTestTools& operator=(const DistributedDBSchemaTestTools &DistributedDBSchemaTestTools) = delete; + DistributedDBSchemaTestTools(DistributedDBSchemaTestTools &&DistributedDBSchemaTestTools) = delete; + DistributedDBSchemaTestTools& operator=(DistributedDBSchemaTestTools &&DistributedDBSchemaTestTools) = delete; + static DistributedDB::Entry GenerateFixedLenSchemaRecord(const unsigned long serialNo, + const EntrySize &entrySize, const uint8_t keyFilledChr, const uint8_t valueFilledChr); + static std::vector GenerateFixedSchemaRecords(std::vector &allKeys, + const int recordNum, const EntrySize &entrySize, const uint8_t keyFilledChr, const uint8_t valueFilledChr); + static DistributedDB::Entry GenerateFixedLenSchemaPerfRecord(const uint64_t presetRecordsCnt, + const uint64_t serialNo, const RecordInfo &recordInfo, + const std::string &valueSkipString = VALUE_SKIP_STRING); +}; +#endif // DISTRIBUTED_DB_SCHEMA_TEST_TOOLS_H \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/src/distributed_test_sysinfo.cpp b/services/distributeddataservice/test/common/distributeddb/src/distributed_test_sysinfo.cpp new file mode 100755 index 000000000..c2cbe2b58 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/src/distributed_test_sysinfo.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributed_test_sysinfo.h" +#include "distributeddb_data_generator.h" +#include "securec.h" + +const uint64_t PERCENTAGE_FACTOR = 100; +const float FLOAT_RET_ERROR = -1.0f; + +DistributedTestSysInfo::DistributedTestSysInfo() +{ + if ((memset_s(&memStatFirst_, sizeof(memStatFirst_), 0, sizeof(memStatFirst_)) != EOK) && + (memset_s(&memStatSecond_, sizeof(memStatSecond_), 0, sizeof(memStatSecond_)) != EOK) && + (memset_s(&cpuStatFirst_, sizeof(cpuStatFirst_), 0, sizeof(cpuStatFirst_)) != EOK) && + (memset_s(&cpuStatSecond_, sizeof(cpuStatSecond_), 0, sizeof(cpuStatSecond_)) != EOK)) { + MST_LOG("distribute info set 0 failed!"); + } + + cpuStatFirstUsage_ = 0.0f; + cpuStatSecondUsage_ = 0.0f; + powerStatFirst_ = 0.0f; + powerStatSecond_ = 0.0f; + val_ = 0.0f; +} + +void DistributedTestSysInfo::GetSysMemOccpy(SeqNo seqNo) +{ + MemOccupy* memOccupy = nullptr; + if (seqNo == FIRST) { + memOccupy = &memStatFirst_; + } else { + memOccupy = &memStatSecond_; + } + + FILE *fp = nullptr; + char buff[PROC_BUFFER_LENGTH] = { 0 }; + + fp = fopen(SYS_MEM_FILE.c_str(), FILE_READ_PERMISSION.c_str()); + if (fp == nullptr) { + perror("fopen:"); + return; + } + + fgets(buff, sizeof(buff), fp); + int result1 = sscanf_s(buff, "%s %llu ", + memOccupy->memTotalName_, sizeof(memOccupy->memTotalName_), &memOccupy->memTotal_); + fgets(buff, sizeof(buff), fp); + int result2 = sscanf_s(buff, "%s %llu ", + memOccupy->memFreeName_, sizeof(memOccupy->memFreeName_), &memOccupy->memFree_); + fgets(buff, sizeof(buff), fp); + int result3 = sscanf_s(buff, "%s %llu ", + memOccupy->buffersName_, sizeof(memOccupy->buffersName_), &memOccupy->buffers_); + fgets(buff, sizeof(buff), fp); + int result4 = sscanf_s(buff, "%s %llu ", + memOccupy->cachedName_, sizeof(memOccupy->cachedName_), &memOccupy->cached_); + fgets(buff, sizeof(buff), fp); + int result5 = sscanf_s(buff, "%s %llu", + memOccupy->swapCachedName_, sizeof(memOccupy->swapCachedName_), &memOccupy->swapCached_); + if (result1 != 2 || result2 != 2) { // there are 2 incoming param. + MST_LOG("get mem1 info failed."); + } + if (result3 != 2 || result4 != 2 || result5 != 2) { // there are 2 incoming param. + MST_LOG("get mem2 info failed."); + } + fclose(fp); + fp = nullptr; +} + +void GetSysCpuInfo(CpuOccupy &cpuStatFirst_, CpuOccupy &cpuStatSecond_, uint64_t microSeconds, float *&cpuStatUsage) +{ + FILE *fp = nullptr; + char buff[PROC_BUFFER_LENGTH] = { 0 }; + uint64_t allFirst, allSecond, idleFirst, idleSecond; + fp = fopen(SYS_CPU_FILE.c_str(), FILE_READ_PERMISSION.c_str()); + if (fp == nullptr) { + perror("fopen:"); + exit(0); + } + fgets(buff, sizeof(buff), fp); + int result = sscanf_s(buff, "%s %llu %llu %llu %llu %llu %llu %llu", + cpuStatFirst_.name_, sizeof(cpuStatFirst_.name_), + &cpuStatFirst_.user_, &cpuStatFirst_.nice_, + &cpuStatFirst_.system_, &cpuStatFirst_.idle_, &cpuStatFirst_.iowait_, + &cpuStatFirst_.irq_, &cpuStatFirst_.softirq_); + if (result != 8) { // there are 8 incoming param. + fclose(fp); + return; + } + allFirst = cpuStatFirst_.user_ + cpuStatFirst_.nice_ + cpuStatFirst_.system_ + cpuStatFirst_.idle_ \ + + cpuStatFirst_.iowait_ + cpuStatFirst_.irq_ + cpuStatFirst_.softirq_; + idleFirst = cpuStatFirst_.idle_; + rewind(fp); + std::this_thread::sleep_for(std::chrono::microseconds(microSeconds)); + if (memset_s(buff, sizeof(buff), 0, sizeof(buff)) != EOK) { + MST_LOG("set buffer to 0 failed!"); + fclose(fp); + return; + } + fgets(buff, sizeof(buff), fp); + result = sscanf_s(buff, "%s %llu %llu %llu %llu %llu %llu %llu", + cpuStatSecond_.name_, sizeof(cpuStatSecond_.name_), + &cpuStatSecond_.user_, &cpuStatSecond_.nice_, + &cpuStatSecond_.system_, &cpuStatSecond_.idle_, &cpuStatSecond_.iowait_, + &cpuStatSecond_.irq_, &cpuStatSecond_.softirq_); + if (result < 0) { + fclose(fp); + return; + } + + allSecond = cpuStatSecond_.user_ + cpuStatSecond_.nice_ + cpuStatSecond_.system_ + cpuStatSecond_.idle_ \ + + cpuStatSecond_.iowait_ + cpuStatSecond_.irq_ + cpuStatSecond_.softirq_; + idleSecond = cpuStatSecond_.idle_; + *cpuStatUsage = (static_cast(allSecond - allFirst - (idleSecond - idleFirst))) + / (allSecond - allFirst) * PERCENTAGE_FACTOR; + MST_LOG(" [CpuUsage] = %.2f%%", *cpuStatUsage); + fclose(fp); + fp = nullptr; + return; +} +void DistributedTestSysInfo::GetSysCpuUsage(SeqNo seqNo, uint64_t microSeconds) +{ + float *cpuStatUsage = nullptr; + if (seqNo == FIRST) { + cpuStatUsage = &cpuStatFirstUsage_; + } else { + cpuStatUsage = &cpuStatSecondUsage_; + } + GetSysCpuInfo(cpuStatFirst_, cpuStatSecond_, microSeconds, cpuStatUsage); + if ((memset_s(&cpuStatFirst_, sizeof(cpuStatFirst_), 0, sizeof(cpuStatFirst_)) != EOK) && + (memset_s(&cpuStatSecond_, sizeof(cpuStatSecond_), 0, sizeof(cpuStatSecond_)) != EOK)) { + MST_LOG("set cpu to 0 failed!"); + } +} + +float DistributedTestSysInfo::ReadSysValFromFile(const std::string &filePath) +{ + FILE *fp = nullptr; + char buff[SYSTEM_INFO_BUFFER_SIZE] = { 0 }; + + char path[PATH_MAX] = { 0 }; + if (realpath(filePath.c_str(), path) == nullptr) { + MST_LOG("path error."); + return FLOAT_RET_ERROR; + } + + fp = fopen(filePath.c_str(), FILE_READ_PERMISSION.c_str()); + if (fp == nullptr) { + perror("fopen:"); + return FLOAT_RET_ERROR; + } + + fgets(buff, sizeof(buff), fp); + if (sscanf_s(buff, "%f", &val_) != 1) { + fclose(fp); + return FLOAT_RET_ERROR; + } + fclose(fp); + return val_; +} + +float DistributedTestSysInfo::GetSysMeanCurrentVal( + const std::string &filePath, int totalCount, uint64_t microSeconds) +{ + float meanVal = 0.0f; + if (totalCount <= 0 || microSeconds <= 0) { + return 0.0f; + } + + for (int cnt = 0; cnt < totalCount; cnt++) { + float val = ReadSysValFromFile(filePath.c_str()); + if (val < 1e-4) { + continue; + } + meanVal += val; + std::this_thread::sleep_for(std::chrono::microseconds(microSeconds)); + } + return meanVal / totalCount; +} + +void DistributedTestSysInfo::GetSysCurrentPower(SeqNo seqNo, int totalCount, uint64_t microSeconds) +{ + float* powerCons = nullptr; + if (seqNo == FIRST) { + powerCons = &powerStatFirst_; + } else { + powerCons = &powerStatSecond_; + } + + *powerCons = GetSysMeanCurrentVal(POWER_FOLLOW_FILE, totalCount, microSeconds) \ + * GetSysMeanCurrentVal(VOLTAGE_FILE, totalCount, microSeconds); + MST_LOG(" [Power] = %.2f", *powerCons); +} + +uint64_t DistributedTestSysInfo::GetFirstMemFree() const +{ + return memStatFirst_.memFree_; +} + +uint64_t DistributedTestSysInfo::GetSecondMemFree() const +{ + return memStatSecond_.memFree_; +} + +float DistributedTestSysInfo::GetFirstCpuUsage() const +{ + return cpuStatFirstUsage_; +} + +float DistributedTestSysInfo::GetSecondCpuUsage() const +{ + return cpuStatSecondUsage_; +} + +float DistributedTestSysInfo::GetFirstPower() const +{ + return powerStatFirst_; +} + +float DistributedTestSysInfo::GetSecondPower() const +{ + return powerStatSecond_; +} + +void DistributedTestSysInfo::SaveSecondToFirst() +{ + if ((memcpy_s(&memStatFirst_, sizeof(memStatFirst_), &memStatSecond_, sizeof(struct MemOccupy)) != EOK) && + memcpy_s(&cpuStatFirst_, sizeof(cpuStatFirst_), &cpuStatSecond_, sizeof(struct CpuOccupy)) != EOK) { + MST_LOG("set mem or cpu state failed."); + } + cpuStatFirstUsage_ = cpuStatSecondUsage_; + powerStatFirst_ = powerStatSecond_; +} \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/src/distributed_test_tools.cpp b/services/distributeddataservice/test/common/distributeddb/src/distributed_test_tools.cpp new file mode 100755 index 000000000..b83154d51 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/src/distributed_test_tools.cpp @@ -0,0 +1,2056 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_test_tools.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef USE_SQLITE_SYMBOLS +#include "sqlite3.h" +#else +#include "sqlite3sym.h" +#endif +#include "distributeddb_data_generator.h" +#include "distributed_test_sysinfo.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "platform_specific.h" +#include "securec.h" + +using namespace std; +using namespace chrono; +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace { + const std::string CIPHER_CONFIG_SQL = "PRAGMA codec_cipher='aes-256-gcm';"; + const std::string KDF_ITER_CONFIG_SQL = "PRAGMA codec_kdf_iter=5000;"; +} + +bool CompareVector(const std::vector& first, const std::vector& second) +{ + if (first.size() != second.size()) { + MST_LOG("[CompareVector] first.size[%zu] != second.size[%zu]", first.size(), second.size()); + return false; + } + return first == second; +} + +// although the Keys in Entries can be the same in theory, +// but they are different here. +bool CompareList(const std::list& retLst, const std::list& lst) +{ + if (retLst.size() != lst.size()) { + MST_LOG("retLst.size[%zd] != lst.size[%zd]", retLst.size(), lst.size()); + return false; + } + + vector retVector(retLst.begin(), retLst.end()); + vector expectVec(lst.begin(), lst.end()); + sort(retVector.begin(), retVector.end(), DistributedTestTools::CompareKey); + sort(expectVec.begin(), expectVec.end(), DistributedTestTools::CompareKey); + bool result = true; + for (uint64_t at = 0; at < retVector.size(); at++) { + result = ((retVector[at].key == expectVec[at].key) && (retVector[at].value == expectVec[at].value)); + if (!result) { + return result; + } + } + return true; +} + +// although the Keys in Entries can be the same in theory, +// but they are different here. +bool CompareEntriesVector(std::vector& retVec, + std::vector& expectVec) +{ + if (retVec.size() != expectVec.size()) { + MST_LOG("retVec.size() = %zd, expectVec.size() = %zd", retVec.size(), expectVec.size()); + return false; + } + + for (const auto &retEntry : retVec) { + auto it = std::find_if(expectVec.begin(), expectVec.end(), [retEntry](DistributedDB::Entry entry_) { + return (retEntry.key == entry_.key) && (retEntry.value == entry_.value); + }); + if (it == expectVec.end()) { + MST_LOG("Can't find expect Entry in expectVec!"); + return false; + } + } + return true; +} + +void PutUniqueKey(vector &entryVec, const Key &putKey, const Value &putValue) +{ + bool findEachEntry = false; + for (unsigned int idxEntry = 0; idxEntry < entryVec.size(); ++idxEntry) { + if (CompareVector(entryVec[idxEntry].key, putKey)) { + findEachEntry = true; + entryVec[idxEntry].value = putValue; + break; + } + } + Entry entry = { putKey, putValue }; + if (!findEachEntry) { + entryVec.push_back(entry); + } +} + +int Uint8VecToString(std::vector &vec, std::string &str) +{ + int len = 0; + str.clear(); + + for (auto vecIt = vec.begin(); vecIt != vec.end(); ++vecIt) { + str.push_back(static_cast(*vecIt)); + ++len; + } + + return len; +} + +vector GenRanKeyVal(int putGetTimes, int keyLength, int valueLength, char val) +{ + // random gen the key + std::random_device randDevKeyNo; + std::mt19937 genRandKeyNo(randDevKeyNo()); + std::uniform_int_distribution disRandKeyNo(CHAR_SPAN_MIN, CHAR_SPAN_MAX); + + DistributedDB::Entry entryCurrent; + vector entriesBatch; + + for (int cnt = 0; cnt < putGetTimes; ++cnt) { + DistributedDB::Key tempKey = PERFORMANCEKEY; + for (int kIndex = 0; kIndex < keyLength; ++kIndex) { + tempKey.push_back(disRandKeyNo(genRandKeyNo)); + } + entryCurrent.key = tempKey; + entryCurrent.value.assign(valueLength, val); + + entriesBatch.push_back(entryCurrent); + } + return entriesBatch; +} + +bool GetRandBool() +{ + std::random_device randDev; + std::mt19937 genRand(randDev()); + std::uniform_int_distribution disRand(RAND_BOOL_MIN, RAND_BOOL_MAX); + return disRand(genRand); +} + +bool PutEntries(KvStoreNbDelegate *&delegate, vector &entries) +{ + for (const auto &entry : entries) { + if (delegate->Put(entry.key, entry.value) != OK) { + return false; + } + } + return true; +} + +vector GetKeysFromEntries(std::vector < DistributedDB::Entry > entries, bool random) +{ + vector result; + vector old; + old.assign(entries.begin(), entries.end()); + if (random) { + for (int i = entries.size(); i > 0; i--) { + int index = GetRandInt(0, ONE_HUNDRED_SECONDS * MILLSECONDES_PER_SECOND) % i; + result.push_back(old[index].key); + old.erase(old.begin() + index); + } + } else { + for (auto entry = entries.begin(); entry != entries.end(); ++entry) { + result.push_back(entry->key); + } + } + return result; +} + +int GetIntValue(Value &value) +{ + if (value.size() == 0) { + return 0; + } + string strVal; + Uint8VecToString(value, strVal); + int number; + if (sscanf_s(strVal.c_str(), "%d", &number) != 1) { + return 0; + } + return number; +} + +Value GetValueWithInt(int val) +{ + string strVal = std::to_string(val); + Value result; + result.assign(strVal.begin(), strVal.end()); + return result; +} + +vector< vector > GetGroupEntries(vector entries, int pageSize, int valueLength, uint8_t c) +{ + vector< vector > result; + if (pageSize <= 0) { + MST_LOG("pageSize can't be zero or negative number!"); + } else { + int pages = (entries.size() - 1) / pageSize + 1; + for (int pageIndex = 0; pageIndex < pages; ++pageIndex) { + vector temp; + int pageStart = pageSize * pageIndex; + int pageEnd = (pageSize * (pageIndex + 1)) - 1; + if (pageEnd + 1 > static_cast(entries.size())) { + pageEnd = entries.size() - 1; + } + MST_LOG("The %d page start position: %d, end position: %d", pageIndex, pageStart, pageEnd); + temp.assign(entries.begin() + pageStart, entries.begin() + pageEnd); + result.push_back(temp); + } + for (auto iter = result.begin(); iter != result.end(); ++iter) { + for (auto entry = iter->begin(); entry != iter->end(); ++entry) { + entry->value.assign(valueLength, c); + } + } + } + return result; +} + +void DelegateCallback::Callback(DBStatus status, KvStoreSnapshotDelegate *kvStoreSnapshotDelegate) +{ + this->status_ = status; + this->kvStoreSnapshotDelegate_ = kvStoreSnapshotDelegate; +} +DBStatus DelegateCallback::GetStatus() +{ + return status_; +} + +// KvCallback conclude the Callback of function and )> +class KvCallback { +public: + KvCallback() {} + ~KvCallback() {} + + // Delete the copy and assign constructors + KvCallback(const KvCallback &callback) = delete; + KvCallback& operator=(const KvCallback &callback) = delete; + KvCallback(KvCallback &&callback) = delete; + KvCallback& operator=(KvCallback &&callback) = delete; + + void Callback(DBStatus status, const Value &value) + { + this->status_ = status; + this->value_ = value; + } + + void CallbackPrefix(DBStatus status, const vector &entries) + { + this->status_ = status; + this->entries_ = entries; + } + + DBStatus GetStatus() + { + return status_; + } + + const Value &GetValue() + { + return value_; + } + + const vector &GetValueVector() + { + return entries_; + } + +private: + DBStatus status_ = DBStatus::INVALID_ARGS; + Value value_ = { }; + vector entries_ = {}; +}; + +// delete dir(directory) recursively +int RemoveDir(const std::string &dirSrc) +{ + string directory = dirSrc; + char curDir[] = "."; + char upDir[] = ".."; + char dirName[MAX_DIR_LENGTH] = { 0 }; + DIR *dirp = nullptr; + struct dirent *dirPointer = nullptr; + struct stat dirStat; + + while (directory.at(directory.length() - 1) == '/') { + directory.pop_back(); + } + + const char *dir = directory.c_str(); + // if dir is not existed + if (access(dir, F_OK) != 0) { + return 0; + } + // getting dir attribution fails + if (stat(dir, &dirStat) < 0) { + perror("get directory stat error"); + return -1; + } + // if dir is a common file + if (S_ISREG(dirStat.st_mode)) { + remove(dir); + } else if (S_ISDIR(dirStat.st_mode)) { + // if dir is directory, delete it recursively + dirp = opendir(dir); + if (dirp == nullptr) { + perror("open directory error"); + return -1; + } + while ((dirPointer = readdir(dirp)) != nullptr) { + // ignore . and .. + if ((strcmp(curDir, dirPointer->d_name) == 0) || (strcmp(upDir, dirPointer->d_name) == 0)) { + continue; + } + int result = sprintf_s(dirName, sizeof(dirName) - 1, "%s/%s", dir, dirPointer->d_name); + if (result < 0) { + closedir(dirp); + return -1; + } + MST_LOG("delete dirName = %s", dirName); + RemoveDir(std::string(dirName)); + } + closedir(dirp); + } else { + perror("unknown file type!"); + } + return 0; +} + +// If the test cases' related dir in system is not exist, create it. +int SetDir(const std::string &directory, const int authRight) +{ + char dir[MAX_DIR_LENGTH] = { 0 }; + int i, len; + + errno_t ret = strcpy_s(dir, MAX_DIR_LENGTH, directory.c_str()); + if (ret != E_OK) { + MST_LOG("strcpy_s failed(%d)!", ret); + return -1; + } + len = strlen(dir); + if (dir[len - 1] != '/') { + ret = strcat_s(dir, sizeof(dir) - strlen(dir) - 1, "/"); + if (ret != E_OK) { + MST_LOG("strcat_s failed(%d)!", ret); + return -1; + } + len++; + } + + for (i = 1; i < len; i++) { + if (dir[i] != '/') { + continue; + } + dir[i] = '\0'; + if (access(dir, F_OK) != E_OK) { + mode_t mode = umask(0); + if (mkdir(dir, authRight) == E_ERROR) { + MST_LOG("mkdir(%s) failed(%d)!", dir, errno); + return -1; + } + if (chdir(dir) == E_ERROR) { + MST_LOG("chdir(%s) failed(%d)!", dir, errno); + } + umask(mode); + } + dir[i] = '/'; + } + MST_LOG("check dir = %s", dir); + return 0; +} + +void CheckFileNumber(const string &filePath, int &fileCount) +{ + std::string cmd = "cd " + filePath + ";ls -l |grep " + "^-" + "|wc -l"; + FILE *pipe = popen(cmd.c_str(), "r"); + if (pipe == nullptr) { + MST_LOG("Check file number filed."); + return; + } + + char buffer[PIPE_BUFFER]; + std::string result = ""; + while (!feof(pipe)) { + if (fgets(buffer, PIPE_BUFFER, pipe) != NULL) { + result += buffer; + } + } + pclose(pipe); + fileCount = std::stoi(result); + MST_LOG("Check file number is %d", fileCount); +} + +// this static method is to compare if the two Value has the same data. +bool DistributedTestTools::IsValueEquals(const DistributedDB::Value &v1, const DistributedDB::Value &v2) +{ + // just return false if the sizes are not the same. + if (v1.size() != v2.size()) { + return false; + } + + // compare two Values char by char. + return v1 == v2; +} + +// This static method is for moduleTest of distributed to get KvStoreDelegate with valid ids. +KvStoreDelegate* DistributedTestTools::GetDelegateSuccess(KvStoreDelegateManager *&outManager, + const KvDBParameters ¶m, const KvOption &optionParam) +{ + MST_LOG("GetDelegateSuccess isEncryptedDb= %d", optionParam.isEncryptedDb); + SetDir(DIRECTOR); + if (param.storeId.empty() || param.appId.empty() || param.userId.empty()) { + return nullptr; + } + + // define a Callback to hold the kvStoreDelegate and status. + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, _1, _2); + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + if (outManager != nullptr) { + delete outManager; + outManager = nullptr; + } + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(param.appId, param.userId); + if (manager == nullptr) { + MST_LOG("new delegate failed nullptr."); + return nullptr; + } + DBStatus status = manager->SetKvStoreConfig(KV_CONFIG); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return nullptr; + } + + KvStoreDelegate::Option option = TransferKvOptionType(optionParam); + // get kv store, then the Callback will save the status and delegate. + manager->GetKvStore(param.storeId, option, function); + status = delegateKvMgrCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return nullptr; + } + const KvStoreDelegate* delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + if (delegate == nullptr) { + MST_LOG("%s GetKvStore failed! delegate nullptr.", TAG.c_str()); + delete manager; + manager = nullptr; + return nullptr; + } + + MST_LOG("%s GetKvStore success: %s %s %s %d %d", TAG.c_str(), + param.storeId.c_str(), param.appId.c_str(), param.userId.c_str(), option.createIfNecessary, option.localOnly); + outManager = manager; + return const_cast(delegate); +} + +KvStoreDelegate* DistributedTestTools::GetDelegateStatus(KvStoreDelegateManager *&outManager, DBStatus &statusReturn, + const KvDBParameters ¶m, const KvOption &optionParam) +{ + MST_LOG("Delegate isEncryptedDb : %s", (optionParam.isEncryptedDb ? "true" : "false")); + SetDir(DIRECTOR); + if (param.storeId.empty() || param.appId.empty() || param.userId.empty()) { + return nullptr; + } + + // define a Callback to hold the kvStoreDelegate and status. + DelegateKvMgrCallback delegateKvMgrCallback; + function functionCallback + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, _1, _2); + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + if (outManager != nullptr) { + delete outManager; + outManager = nullptr; + } + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(param.appId, param.userId); + if (manager == nullptr) { + MST_LOG("new delegate failed nullptr."); + return nullptr; + } + DBStatus status = manager->SetKvStoreConfig(KV_CONFIG); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return nullptr; + } + + KvStoreDelegate::Option option = TransferKvOptionType(optionParam); + // get kv store, then the Callback will save the status and delegate. + manager->GetKvStore(param.storeId, option, functionCallback); + statusReturn = delegateKvMgrCallback.GetStatus(); + if (statusReturn != DBStatus::OK) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), statusReturn); + delete manager; + manager = nullptr; + return nullptr; + } + const KvStoreDelegate* delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + if (delegate == nullptr) { + MST_LOG("%s GetDelegate failed! delegate is nullptr.", TAG.c_str()); + delete manager; + manager = nullptr; + return nullptr; + } + + MST_LOG("%s GetKvStore success : %s %s %s %d %d", TAG.c_str(), param.storeId.c_str(), param.appId.c_str(), + param.userId.c_str(), option.createIfNecessary, option.localOnly); + outManager = manager; + return const_cast(delegate); +} + +// This static method is for moduleTest of distributed to try-get KvStoreDelegate with invalid params. +DBStatus DistributedTestTools::GetDelegateNotGood(KvStoreDelegateManager *&manager, KvStoreDelegate *&outDelegate, + const string &storeId, const string &appId, const string &userId, const KvOption &optionParam) +{ + SetDir(DIRECTOR); + + // define a Callback to hold the kvStoreDelegate and status. + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, _1, _2); + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + manager = new (std::nothrow) KvStoreDelegateManager(appId, userId); + if (manager == nullptr) { + MST_LOG("new delegate failed nullptr."); + return DBStatus::DB_ERROR; + } + DBStatus status = manager->SetKvStoreConfig(KV_CONFIG); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + status = DBStatus::INVALID_ARGS; + delete manager; + manager = nullptr; + return status; + } + + KvStoreDelegate::Option option = TransferKvOptionType(optionParam); + // get kv store, then the Callback will save the status and delegate. + manager->GetKvStore(storeId, option, function); + status = delegateKvMgrCallback.GetStatus(); + outDelegate = const_cast(delegateKvMgrCallback.GetKvStore()); + if (outDelegate != nullptr) { + MST_LOG("%s GetKvStore failed! delegate nullptr.", TAG.c_str()); + delete manager; + manager = nullptr; + return status; + } + if (status == DBStatus::NOT_FOUND) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return status; + } + + MST_LOG("%s GetKvStore failed, Status = %d", TAG.c_str(), status); + return status; +} + +// This static method is for moduleTest to put value with kvStoreDelegate. +DBStatus DistributedTestTools::Put(KvStoreDelegate &kvStoreDelegate, const Key &key, const Value &value) +{ + return kvStoreDelegate.Put(key, value); +} + +// This static method is for moduleTest to putBatch value with kvStoreDelegate. +DBStatus DistributedTestTools::PutBatch(KvStoreDelegate &kvStoreDelegate, const vector &entries) +{ + DistributedDB::DBStatus status; + unsigned int cnt = entries.size(); + int index = 0; + while (cnt > BATCH_RECORDS) { + cnt -= BATCH_RECORDS; + std::vector entriesBatch(entries.begin() + index * BATCH_RECORDS, + entries.begin() + (index + 1) * BATCH_RECORDS); + status = kvStoreDelegate.PutBatch(entriesBatch); + index++; + if (status != DBStatus::OK) { + return status; + } + } + std::vector entriesBatch(entries.begin() + index * BATCH_RECORDS, entries.end()); + status = kvStoreDelegate.PutBatch(entriesBatch); + + return status; +} + +// This static method is for moduleTest to delete value with kvStoreDelegate. +DBStatus DistributedTestTools::Delete(KvStoreDelegate &kvStoreDelegate, const Key &key) +{ + return kvStoreDelegate.Delete(key); +} + +// This static method is for moduleTest to deleteBatch value with kvStoreDelegate. +DBStatus DistributedTestTools::DeleteBatch(KvStoreDelegate &kvStoreDelegate, const vector &keys) +{ + if (keys.size() > BATCH_RECORDS) { + int cnt = 0; + std::vector keyBatch; + DistributedDB::DBStatus status; + for (const auto &iter : keys) { + keyBatch.push_back(iter); + cnt++; + if (cnt % BATCH_RECORDS == 0 || cnt == static_cast(keys.size())) { + status = kvStoreDelegate.DeleteBatch(keyBatch); + if (status != DBStatus::OK) { + return status; + } + keyBatch.clear(); + } + } + return status; + } else { + return kvStoreDelegate.DeleteBatch(keys); + } +} + +// This static method is for moduleTest to clear value with kvStoreDelegate. +DBStatus DistributedTestTools::Clear(KvStoreDelegate &kvStoreDelegate) +{ + return kvStoreDelegate.Clear(); +} + +// This static method is for moduleTest to try-get snapshot with kvStoreDelegate. +KvStoreSnapshotDelegate *DistributedTestTools::GetKvStoreSnapshot(KvStoreDelegate &kvStoreDelegate) +{ + DelegateCallback delegateCallback; + function function + = bind(&DelegateCallback::Callback, &delegateCallback, _1, _2); + + // no need to use obsever, so param1 is nullptr; + kvStoreDelegate.GetKvStoreSnapshot(nullptr, function); + DBStatus status = delegateCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s Get Callback failed! Status= %d", TAG.c_str(), status); + return nullptr; + } + KvStoreSnapshotDelegate *snapshot + = const_cast(delegateCallback.GetKvStoreSnapshot()); + if (snapshot == nullptr) { + MST_LOG("%s Get KvStoreSnapshot null! Status= %d", TAG.c_str(), status); + return nullptr; + } + return snapshot; +} + +// This static method is for moduleTest to get value with kvStoreDelegate. +Value DistributedTestTools::Get(KvStoreDelegate &kvStoreDelegate, const Key &key) +{ + // initialize the result value. + Value result = { }; + DelegateCallback delegateCallback; + function function1 + = bind(&DelegateCallback::Callback, &delegateCallback, _1, _2); + kvStoreDelegate.GetKvStoreSnapshot(nullptr, function1); + DBStatus status = delegateCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s Get failed! Status= %d", TAG.c_str(), status); + return result; + } + + // the first Callback's kvStoreDelegate is used to get snapshot. + KvStoreSnapshotDelegate *snapshot + = const_cast(delegateCallback.GetKvStoreSnapshot()); + if (snapshot == nullptr) { + MST_LOG("%s Get snapshot null! Status= %d", TAG.c_str(), status); + return result; + } + + // the second Callback is used in Snapshot. + KvCallback kvCallback; + function function2 + = bind(&KvCallback::Callback, &kvCallback, _1, _2); + snapshot->Get(key, function2); + status = kvCallback.GetStatus(); + if (status != DBStatus::OK) { + kvStoreDelegate.ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + MST_LOG("%s Get value failed! Status= %d", TAG.c_str(), status); + return result; + } + result = kvCallback.GetValue(); + kvStoreDelegate.ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + return result; +} + +Value DistributedTestTools::Get(KvStoreSnapshotDelegate &kvStoreSnapshotDelegate, const Key &key) +{ + KvCallback kvCallback; + function function2 + = bind(&KvCallback::Callback, &kvCallback, _1, _2); + kvStoreSnapshotDelegate.Get(key, function2); + return kvCallback.GetValue(); +} + +vector DistributedTestTools::GetEntries(KvStoreSnapshotDelegate &kvStoreSnapshotDelegate, const Key &key) +{ + KvCallback kvCallback; + function)> function2 + = bind(&KvCallback::CallbackPrefix, &kvCallback, _1, _2); + kvStoreSnapshotDelegate.GetEntries(key, function2); + return kvCallback.GetValueVector(); +} + +// This static method is for moduleTest to get values with kvStoreDelegate and keyPrefix. +vector DistributedTestTools::GetEntries(KvStoreDelegate &kvStoreDelegate, const Key &keyPrefix) +{ + DelegateCallback delegateCallback; + function function1 + = bind(&DelegateCallback::Callback, &delegateCallback, _1, _2); + kvStoreDelegate.GetKvStoreSnapshot(nullptr, function1); + DBStatus status = delegateCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s Get failed! Status= %d", TAG.c_str(), status); + return {}; + } + + KvStoreSnapshotDelegate *snapshot + = const_cast(delegateCallback.GetKvStoreSnapshot()); + if (snapshot == nullptr) { + MST_LOG("%s Get snapshot null! Status= %d", TAG.c_str(), status); + return {}; + } + + KvCallback kvCallback; + function)> function2 = bind(&KvCallback::CallbackPrefix, &kvCallback, _1, _2); + snapshot->GetEntries(keyPrefix, function2); + status = kvCallback.GetStatus(); + if (status != DBStatus::OK) { + kvStoreDelegate.ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + MST_LOG("%s GetEntries failed! Status= %d", TAG.c_str(), status); + return {}; + } + kvStoreDelegate.ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + return kvCallback.GetValueVector(); +} + +void TickTock(steady_clock::time_point &tick, double &duration) +{ + steady_clock::time_point tock = steady_clock::now(); + auto durate = duration_cast(tock - tick); + tick = tock; + duration = static_cast(durate.count()); +} + +bool TickTock1(steady_clock::time_point &tick, int putGetTimes, double &duration) +{ + if (putGetTimes <= 0) { + MST_LOG("putGetTimes is 0!"); + return false; + } + TickTock(tick, duration); + duration /= putGetTimes; + return true; +} + +void GetSysInfo(DistributedTestSysInfo &si, SeqNo seqNo) +{ + si.GetSysMemOccpy(seqNo); + si.GetSysCpuUsage(seqNo, DEFAULT_INTEVAL); + si.GetSysCurrentPower(seqNo, DEFAULT_COUNT, DEFAULT_INTEVAL); +} + +void CheckBeforeOpenDB(bool getSysInfo, steady_clock::time_point &tick, + vector data1, vector keys, DistributedTestSysInfo &si) +{ + if (!getSysInfo) { + MST_LOG("[gen data]:%zu, %zu.", data1.size(), keys.size()); + tick = time_point_cast(steady_clock::now()); + } else { + MST_LOG("System info before opening a db:"); + GetSysInfo(si, FIRST); + } +} + +void CheckAfterOperateDB(bool getSysInfo, steady_clock::time_point &tick, + double &duration, DistributedTestSysInfo &si) +{ + if (!getSysInfo) { + TickTock(tick, duration); + MST_LOG("[time calculator]this operate cost %f us.", duration); + } else { + MST_LOG("System info after opening db, or before inserting records:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void PutDuration(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + double duration; + if (!getSysInfo) { + TickTock(tick, duration); + performanceData.putDuration = duration / putGetTimes; + MST_LOG("[time calculator]put first [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, duration, performanceData.putDuration); + TickTock(tick, duration); + } else { + MST_LOG("System info after inserting records, or before querying records:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void ReadDuration(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + double duration; + if (!getSysInfo) { + TickTock(tick, duration); + performanceData.readPutDuration = duration / putGetTimes; + MST_LOG("[time calculator]get first [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, duration, performanceData.readPutDuration); + TickTock(tick, duration); + } else { + MST_LOG("System info after querying records, or before updating records:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void UpdateDuration(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + double duration; + if (!getSysInfo) { + TickTock(tick, duration); + performanceData.updateDuration = duration / putGetTimes; + MST_LOG("[time calculator]put second [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, duration, performanceData.updateDuration); + TickTock(tick, duration); + } else { + MST_LOG("System info after updating records, or before querying records one by one:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void ReadUpdateDuration(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + double duration; + if (!getSysInfo) { + TickTock(tick, duration); + performanceData.readUpdateDuration = duration / putGetTimes; + MST_LOG("[time calculator]get second [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, duration, performanceData.readUpdateDuration); + TickTock(tick, duration); + } else { + MST_LOG("System info after querying records one by one, or before deleting records one by one:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void ClearDuration(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + double duration; + if (!getSysInfo) { + TickTock(tick, duration); + performanceData.deleteDuration = duration / putGetTimes; + MST_LOG("[time calculator]delete [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, duration, performanceData.deleteDuration); + TickTock(tick, duration); + } else { + MST_LOG("System info after deleting records one by one, or before closing a db:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +bool DistributedTestTools::CalculateOpenPerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + // print the opened time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + + return true; +} + +bool DistributedTestTools::CalculateInsertPerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool putBatch = performanceData.putBatch; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + vector< vector > groupEntries = GetGroupEntries(data1, BATCH_RECORDS, valueLength, 'a'); + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + // print the insert time. + if (putBatch) { + for (auto entries = groupEntries.begin(); entries != groupEntries.end(); ++entries) { + DistributedTestTools::PutBatch(*store1, *entries); + } + } else { + for (auto entry = data1.begin(); entry != data1.end(); ++entry) { + DistributedTestTools::Put(*store1, entry->key, entry->value); + } + } + + PutDuration(tick, performanceData, keyLength, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + + return true; +} + +bool DistributedTestTools::CalculateGetPutPerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool getBatch = performanceData.getBatch; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + + // print the get put time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::GetKvStoreSnapshot(*store1); + if (snapShot == nullptr) { + return false; + } + if (getBatch) { + std::vector ev = DistributedTestTools::GetEntries(*snapShot, PERFORMANCEKEY); + } else { + for (auto key = keys.begin(); key != keys.end(); ++key) { + Value valueResult = DistributedTestTools::Get(*snapShot, *key); + } + } + store1->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + ReadDuration(tick, performanceData, keyLength, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + + return true; +} + +bool DistributedTestTools::CalculateUpdatePerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool putBatch = performanceData.putBatch; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + Value updateVal; + updateVal.assign(valueLength, 'b'); + vector< vector > groupEntriesUp = GetGroupEntries(data1, BATCH_RECORDS, valueLength, 'b'); + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + + // print the update time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + if (putBatch) { + for (auto entries = groupEntriesUp.begin(); entries != groupEntriesUp.end(); ++entries) { + DistributedTestTools::PutBatch(*store1, *entries); + } + } else { + for (auto entry = data1.begin(); entry != data1.end(); ++entry) { + DistributedTestTools::Put(*store1, entry->key, updateVal); + } + } + UpdateDuration(tick, performanceData, keyLength, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + + return true; +} + +bool DistributedTestTools::CalculateGetUpdatePerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + + // print the get update time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::GetKvStoreSnapshot(*store1); + if (snapShot == nullptr) { + return false; + } + for (auto key = keys.begin(); key != keys.end(); ++key) { + Value valueResult = DistributedTestTools::Get(*snapShot, *key); + } + store1->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + ReadUpdateDuration(tick, performanceData, keyLength, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + return true; +} + +bool DistributedTestTools::CalculateUseClearPerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool useClear = performanceData.useClear; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + + CheckBeforeOpenDB(getSysInfo, tick, data1, keys, si); + + // print the get update time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = DistributedTestTools::GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + if (useClear) { + DistributedTestTools::Clear(*store1); + } else { + for (auto key = keys.begin(); key != keys.end(); ++key) { + DistributedTestTools::Delete(*store1, *key); + } + } + ClearDuration(tick, performanceData, keyLength, si); + + // close the base db + if (manager->CloseKvStore(store1) != DistributedDB::DBStatus::OK) { + return false; + } + if (manager->DeleteKvStore(STORE_ID_PERFORM) != OK) { + return false; + } + store1 = nullptr; + delete manager; + manager = nullptr; + CheckAfterOperateDB(getSysInfo, tick, performanceData.closeDuration, si); + return true; +} + +bool DistributedTestTools::CloseAndRelease(KvStoreDelegateManager *&manager, KvStoreDelegate *&delegate) +{ + bool result = true; + if (delegate != nullptr && manager != nullptr) { + result = (manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + } else { + MST_LOG("Close Failed"); + return false; + } + return result; +} + +void ReleaseSqliteResource(sqlite3_stmt *&statement, sqlite3 *&db) +{ + sqlite3_finalize(statement); + statement = nullptr; + sqlite3_close(db); +} + +bool SqliteBindToStatement(sqlite3_stmt *&statement, sqlite3 *&db, std::vector &sqlParam) +{ + int errCode = 0; + for (unsigned int index = 0; index < sqlParam.size(); index++) { + if (sqlParam[index].empty()) { + errCode = sqlite3_bind_zeroblob(statement, index, -1); // -1 for zero-length blob. + } else { + errCode = sqlite3_bind_blob(statement, index + 1, static_cast(sqlParam[index].data()), + sqlParam[index].size(), SQLITE_TRANSIENT); + } + if (errCode != SQLITE_OK) { + MST_LOG("[SqliteBindToStatement] Failed to bind the SQLite blob: %d", errCode); + ReleaseSqliteResource(statement, db); + return false; + } + } + return true; +} + +static bool SetSQLiteKey(sqlite3 *db, const std::vector &passwd) +{ + int errCode = sqlite3_key(db, static_cast(passwd.data()), + static_cast(passwd.size())); + if (errCode != SQLITE_OK) { + MST_LOG("[DistributedTestTools::SetSQLiteKey] sqlite3_key failed: %d", errCode); + return false; + } + char *errMsg = nullptr; + bool result = false; + errCode = sqlite3_exec(db, CIPHER_CONFIG_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK) { + MST_LOG("[DistributedTestTools::SetSQLiteKey] config cipher failed: %d", errCode); + goto END; + } + + errCode = sqlite3_exec(db, KDF_ITER_CONFIG_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK) { + MST_LOG("[DistributedTestTools::SetSQLiteKey] config kdf iter failed: %d", errCode); + goto END; + } + result = true; +END: + if (errMsg != nullptr) { + sqlite3_free(errMsg); + errMsg = nullptr; + } + return result; +} + +bool DistributedTestTools::GetRecordCntByKey(const std::string &dbName, + const std::string &strSql, std::vector &sqlParam, KvOption &option, int &count) +{ + sqlite3 *db = nullptr; + std::string dbOpenName = dbName; + uint64_t flag = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE; + int errCode = sqlite3_open_v2(dbOpenName.c_str(), &db, flag, nullptr); + if (errCode != SQLITE_OK) { + MST_LOG("[DistributedTestTools::GetRecordCntByKey] sqlite3_open failed: %d", errCode); + return false; + } + + if (option.isEncryptedDb) { + if (SetSQLiteKey(db, option.passwd) != true) { + MST_LOG("[DistributedTestTools::GetRecordCntByKey] sqlite3 set key failed"); + sqlite3_close(db); + return false; + } + } + + sqlite3_stmt *statement = nullptr; + errCode = sqlite3_prepare(db, strSql.c_str(), -1, &statement, nullptr); + if (errCode != SQLITE_OK) { + MST_LOG("[DistributedTestTools::GetRecordCntByKey] sqlite3_prepare failed: %d", errCode); + ReleaseSqliteResource(statement, db); + return false; + } + if (!SqliteBindToStatement(statement, db, sqlParam)) { + return false; + } + errCode = sqlite3_step(statement); + if (errCode == SQLITE_ROW) { + count = static_cast(sqlite3_column_int64(statement, 0)); + } else { + MST_LOG("[DistributedTestTools::GetRecordCntByKey] sqlite3_step failed: %d", errCode); + ReleaseSqliteResource(statement, db); + count = 0; + return false; + } + + ReleaseSqliteResource(statement, db); + return true; +} + +bool DistributedTestTools::QuerySpecifiedData(const std::string &dbName, + const std::string &strSql, EncrypteAttribute &attribute, int &count) +{ + sqlite3 *db = nullptr; + std::string dbOpenName = dbName; + uint64_t flag = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE; + int nResult = sqlite3_open_v2(dbOpenName.c_str(), &db, flag, nullptr); + if (nResult != SQLITE_OK) { + MST_LOG("[sqlite3_open] failed: %d.", nResult); + return false; + } + + if (attribute.isEncryptedDb) { + if (SetSQLiteKey(db, attribute.passwd) != true) { + MST_LOG("[DistributedTestTools::QuerySpecifiedData] sqlite3 set key failed"); + sqlite3_close(db); + return false; + } + } + + sqlite3_stmt *statement = nullptr; + nResult = sqlite3_prepare(db, strSql.c_str(), -1, &statement, nullptr); + if (nResult != SQLITE_OK) { + MST_LOG("[sqlite3_prepare] failed %d.", nResult); + sqlite3_finalize(statement); + statement = nullptr; + sqlite3_close(db); + return false; + } + int errCode = sqlite3_step(statement); + if (errCode != SQLITE_ROW) { + MST_LOG("[sqlite3_step] failed, errCode: %d.", errCode); + sqlite3_finalize(statement); + statement = nullptr; + sqlite3_close(db); + return false; + } + count = static_cast(sqlite3_column_int64(statement, 0)); + MST_LOG("The query count is %d\n.", count); + sqlite3_finalize(statement); + statement = nullptr; + sqlite3_close(db); + return true; +} + +bool DistributedTestTools::RepeatCheckAsyncResult(const std::function &inPred, int repeatLimit, + uint32_t repeatInterval) +{ + int limit = repeatLimit; + while (limit != 0) { + if (inPred()) { + return true; + } + if (--limit <= 0) { + MST_LOG("BREAK RepeatCheckAsyncResult :: limit %d\n", limit); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(repeatInterval)); + } + return false; +} + +bool DistributedTestTools::CompareKey(const Entry &entry1, const Entry &entry2) +{ + return entry1.key < entry2.key; +} + +void DelegateKvMgrCallback::Callback(DistributedDB::DBStatus status, DistributedDB::KvStoreDelegate *kvStoreDelegate) +{ + this->status_ = status; + this->kvStoreDelegate_ = kvStoreDelegate; +} + +DistributedDB::DBStatus DelegateKvMgrCallback::GetStatus() +{ + return status_; +} + +const DistributedDB::KvStoreDelegate *DelegateKvMgrCallback::GetKvStore() +{ + return kvStoreDelegate_; +} + +void TransactionBeforOpenDB(bool getSysInfo, steady_clock::time_point &tick, + vector &data1, vector &keys, DistributedTestSysInfo &si) +{ + if (!getSysInfo) { + MST_LOG("[gen data]:%zu, %zu.", data1.size(), keys.size()); + tick = time_point_cast(steady_clock::now()); + } else { + MST_LOG("System info before opening a db:"); + GetSysInfo(si, FIRST); + } +} + +void TransactionAfterOpenDB(bool getSysInfo, steady_clock::time_point &tick, + PerformanceData &performanceData, DistributedTestSysInfo &si) +{ + if (!getSysInfo) { + TickTock(tick, performanceData.openDuration); + MST_LOG("[time calculator]open a new db cost %f us.", static_cast(performanceData.openDuration)); + } else { + MST_LOG("System info after opening db, or before inserting records of a transaction:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterPutData(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.putDuration)) { + MST_LOG("[time calculator]put first [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, (performanceData.putDuration) * putGetTimes, + performanceData.putDuration); + } + } else { + MST_LOG("System info after inserting records of a transaction, or before querying records one by one:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterQueryPutData(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.readPutDuration)) { + MST_LOG("[time calculator]get first [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, (performanceData.readPutDuration) * putGetTimes, + performanceData.readPutDuration); + } + } else { + MST_LOG("System info after querying records one by one, or before updating records in a transaction:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterUpdateData(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.updateDuration)) { + MST_LOG("[time calculator]put second [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, (performanceData.updateDuration) * putGetTimes, + performanceData.updateDuration); + } + } else { + MST_LOG("System info after updating records in a transaction, or before updating records one by one:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterQueryUpdateData(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.readUpdateDuration)) { + MST_LOG("[time calculator]get second [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, (performanceData.readUpdateDuration) * putGetTimes, + performanceData.readUpdateDuration); + } + } else { + MST_LOG("System info after updating records one by one, or before deleting records in a transaction:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterDeleteData(steady_clock::time_point &tick, PerformanceData &performanceData, + int keyLength, DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.deleteDuration)) { + MST_LOG("[time calculator]delete [%d]keys,\tvalue[%dB-length],\tcost[%fus],\tper[%fus].", + putGetTimes, keyLength, (performanceData.deleteDuration) * putGetTimes, + performanceData.deleteDuration); + } + } else { + MST_LOG("System info after deleting records in a transaction, or before closing a db:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +void TransactionAfterCloseDB(steady_clock::time_point &tick, PerformanceData &performanceData, + DistributedTestSysInfo &si) +{ + bool getSysInfo = performanceData.getSysInfo; + int putGetTimes = performanceData.putGetTimes; + if (!getSysInfo) { + if (!TickTock1(tick, putGetTimes, performanceData.closeDuration)) { + MST_LOG("[time calculator]close a db cost %f us.", static_cast(performanceData.closeDuration)); + } + } else { + MST_LOG("System info after closing a db:"); + GetSysInfo(si, SECOND); + si.SaveSecondToFirst(); + } +} + +bool BeginTransaction1(vector &data1, KvStoreDelegate *store1) +{ + bool result = true; + result = result && (store1->StartTransaction() == DBStatus::OK); + for (auto entry = data1.begin(); entry != data1.end(); ++entry) { + result = result && (DistributedTestTools::Put(*store1, entry->key, entry->value) == DBStatus::OK); + } + result = result && (store1->Commit() == DBStatus::OK); + return result; +} + +bool BeginTransaction2(vector &data1, KvStoreDelegate *store1, Value updateVal) +{ + bool result = true; + result = result && (store1->StartTransaction() == DBStatus::OK); + for (auto entry = data1.begin(); entry != data1.end(); ++entry) { + result = result && (DistributedTestTools::Put(*store1, entry->key, updateVal) == DBStatus::OK); + } + result = result && (store1->Commit() == DBStatus::OK); + return result; +} + +bool BeginTransaction3(KvStoreDelegate *store1, vector &keys) +{ + bool result = true; + result = result && (store1->StartTransaction() == DBStatus::OK); + for (auto key = keys.begin(); key != keys.end(); ++key) { + result = result && (DistributedTestTools::Delete(*store1, *key) == DBStatus::OK); + } + result = result && (store1->Commit() == DBStatus::OK); + return result; +} + +bool CheckSnapShot1(KvStoreSnapshotDelegate *snapShot, KvStoreDelegate *store1, vector &keys) +{ + if (snapShot == nullptr) { + return false; + } + for (auto key = keys.begin(); key != keys.end(); ++key) { + Value valueResult = DistributedTestTools::Get(*snapShot, *key); + } + store1->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + return true; +} + +bool CheckSnapShot2(KvStoreSnapshotDelegate *snapShot, KvStoreDelegate *store1, vector &keys) +{ + snapShot = DistributedTestTools::GetKvStoreSnapshot(*store1); + if (snapShot == nullptr) { + return false; + } + for (auto key = keys.begin(); key != keys.end(); ++key) { + Value valueResult = DistributedTestTools::Get(*snapShot, *key); + } + store1->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + return true; +} + +bool CloseDB(KvStoreDelegateManager *manager, KvStoreDelegate *store1) +{ + DBStatus status = manager->CloseKvStore(store1); + if (status != DistributedDB::DBStatus::OK) { + return false; + } + store1 = nullptr; + status = manager->DeleteKvStore(STORE_ID_PERFORM); + if (status != DistributedDB::DBStatus::OK) { + return false; + } + delete manager; + manager = nullptr; + return true; +} + +bool DistributedTestTools::CalculateTransactionPerformance(PerformanceData &performanceData) +{ + int putGetTimes = performanceData.putGetTimes; + int keyLength = performanceData.keyLength; + int valueLength = performanceData.valueLength; + bool getSysInfo = performanceData.getSysInfo; + DistributedTestSysInfo si; + steady_clock::time_point tick; + vector data1 = GenRanKeyVal(putGetTimes, keyLength, valueLength, 'a'); + vector keys = GetKeysFromEntries(data1, false); + Value updateVal; + updateVal.assign(valueLength, 'b'); + TransactionBeforOpenDB(getSysInfo, tick, data1, keys, si); + // print the opened time. + KvStoreDelegate *store1 = nullptr; + KvStoreDelegateManager *manager = nullptr; + KvOption option = g_kvOption; + option.localOnly = performanceData.isLocal; + store1 = GetDelegateSuccess(manager, KVDB_PARAMETER_PERFORM, option); + if (store1 == nullptr) { + return false; + } + TransactionAfterOpenDB(getSysInfo, tick, performanceData, si); + // print the insert time. + BeginTransaction1(data1, store1); + TransactionAfterPutData(tick, performanceData, keyLength, si); + // print the get time. + KvStoreSnapshotDelegate *snapShot; + snapShot = DistributedTestTools::GetKvStoreSnapshot(*store1); + if (!(CheckSnapShot1(snapShot, store1, keys))) { + return false; + } + TransactionAfterQueryPutData(tick, performanceData, keyLength, si); + // print the update time. + BeginTransaction2(data1, store1, updateVal); + TransactionAfterUpdateData(tick, performanceData, keyLength, si); + // print the get time. + if (!(CheckSnapShot2(snapShot, store1, keys))) { + return false; + } + TransactionAfterQueryUpdateData(tick, performanceData, keyLength, si); + BeginTransaction3(store1, keys); + TransactionAfterDeleteData(tick, performanceData, keyLength, si); + // close the base db + if (!(CloseDB(manager, store1))) { + return false; + } + TransactionAfterCloseDB(tick, performanceData, si); + return true; +} + +KvStoreSnapshotDelegate *DistributedTestTools::RegisterSnapObserver(KvStoreDelegate *delegate, + KvStoreObserver *observer) +{ + DelegateCallback delegateCallback; + function function + = bind(&DelegateCallback::Callback, &delegateCallback, _1, _2); + + delegate->GetKvStoreSnapshot(observer, function); + DBStatus status = delegateCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s Get failed! Status= %d", TAG.c_str(), status); + return nullptr; + } + + KvStoreSnapshotDelegate *snapshot + = const_cast(delegateCallback.GetKvStoreSnapshot()); + if (snapshot == nullptr) { + MST_LOG("%s Get snapshot null! Status= %d", TAG.c_str(), status); + return nullptr; + } + return snapshot; +} + +DBStatus DistributedTestTools::RegisterObserver(KvStoreDelegate *delegate, KvStoreObserver *observer) +{ + return delegate->RegisterObserver(observer); +} + +DBStatus DistributedTestTools::UnRegisterObserver(KvStoreDelegate *delegate, KvStoreObserver *observer) +{ + return delegate->UnRegisterObserver(observer); +} + +KvStoreDelegate::Option DistributedTestTools::TransferKvOptionType(const KvOption &optionParam) +{ + KvStoreDelegate::Option option; + option.createIfNecessary = optionParam.createIfNecessary; + option.localOnly = optionParam.localOnly; + option.isEncryptedDb = optionParam.isEncryptedDb; + option.cipher = optionParam.cipher; + (void)option.passwd.SetValue(optionParam.passwd.data(), optionParam.passwd.size()); + return option; +} + +std::string TransferStringToHashHexString(const std::string &origStr) +{ + SHA256_CTX contex; + SHA256_Init(&contex); + SHA256_Update(&contex, origStr.data(), origStr.size()); + std::vector hashVect(SHA256_DIGEST_LENGTH, 0); + SHA256_Final(hashVect.data(), &contex); + + const char *hex = "0123456789abcdef"; + std::string tmp; + for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { + tmp.push_back(hex[hashVect[i] >> 4]); // high 4 bit to one hex. + tmp.push_back(hex[hashVect[i] & 0x0F]); // low 4 bit to one hex. + } + return tmp; +} + +int RemoveDatabaseDirectory(const std::string &directory) +{ + return remove(directory.c_str()); +} + +void KvStoreObserverImpl::OnChange(const DistributedDB::KvStoreChangedData &data) +{ + onChangeTime_ = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + insertedEntries_ = data.GetEntriesInserted(); + updatedEntries_ = data.GetEntriesUpdated(); + deleteEntries_ = data.GetEntriesDeleted(); + MST_LOG("insertedEntries_.size():%zu, updatedEntries_.size():%zu, deleteEntries_.size():%zu", + insertedEntries_.size(), updatedEntries_.size(), deleteEntries_.size()); + if (isSaveCumulatedData_) { + if (insertedEntries_.size() != 0) { + for (const auto &entry : insertedEntries_) { + cumulatedInsertList_.push_back(entry); + } + } + if (updatedEntries_.size() != 0) { + for (const auto &entry : updatedEntries_) { + cumulatedUpdateList_.push_back(entry); + } + } + if (deleteEntries_.size() != 0) { + for (const auto &entry : deleteEntries_) { + cumulatedDeleteList_.push_back(entry); + } + } + } + { + std::unique_lock waitChangeLock(waitChangeMutex_); + changed_++; + MST_LOG("comes a change,changed[%d]!!!", changed_); + } + waitChangeCv_.notify_all(); +} + +KvStoreObserverImpl::KvStoreObserverImpl() +{ + onChangeTime_ = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + isSaveCumulatedData_ = false; + Clear(); +} + +KvStoreObserverImpl::~KvStoreObserverImpl() +{ +} + +KvStoreObserverImpl::KvStoreObserverImpl(const KvStoreObserverImpl &other) +{ + insertedEntries_ = other.insertedEntries_; + updatedEntries_ = other.updatedEntries_; + deleteEntries_ = other.deleteEntries_; + changed_ = other.changed_; + onChangeTime_ = other.onChangeTime_; + isSaveCumulatedData_ = other.isSaveCumulatedData_; + cumulatedInsertList_ = other.cumulatedInsertList_; + cumulatedUpdateList_ = other.cumulatedUpdateList_; + cumulatedDeleteList_ = other.cumulatedDeleteList_; +} + +KvStoreObserverImpl& KvStoreObserverImpl::operator=(const KvStoreObserverImpl &other) +{ + if (&other != this) { + insertedEntries_ = other.insertedEntries_; + updatedEntries_ = other.updatedEntries_; + deleteEntries_ = other.deleteEntries_; + changed_ = other.changed_; + onChangeTime_ = other.onChangeTime_; + isSaveCumulatedData_ = other.isSaveCumulatedData_; + cumulatedInsertList_ = other.cumulatedInsertList_; + cumulatedUpdateList_ = other.cumulatedUpdateList_; + cumulatedDeleteList_ = other.cumulatedDeleteList_; + } + return *this; +} + +const std::list KvStoreObserverImpl::GetInsertList() const +{ + return insertedEntries_; +} + +const std::list KvStoreObserverImpl::GetUpdateList() const +{ + return updatedEntries_; +} + +const std::list KvStoreObserverImpl::GetDeleteList() const +{ + return deleteEntries_; +} + +int KvStoreObserverImpl::GetChanged() const +{ + return changed_; +} + +void KvStoreObserverImpl::WaitUntilReachChangeCount(unsigned int countGoal, uint32_t timeout) const // timeout in second +{ + if (countGoal == 0 || changed_ >= countGoal) { + return; + } + // Change count has not reach countGoal + auto waitChangeFunc = [this, countGoal]()->bool { + MST_LOG("############################ realChanged=%d, countGoal=%d", this->changed_, countGoal); + return this->changed_ >= countGoal; + }; + MST_LOG("############################ BEGIN ############################"); + std::unique_lock waitChangeLock(waitChangeMutex_); + if (timeout == 0) { + waitChangeCv_.wait(waitChangeLock, waitChangeFunc); + } else { + waitChangeCv_.wait_for(waitChangeLock, std::chrono::seconds(timeout), waitChangeFunc); + } + MST_LOG("############################ E N D ############################"); +} + +void KvStoreObserverImpl::WaitUntilReachRecordCount(unsigned int countExpect, ListType waitWhat, uint32_t timeout) const +{ + std::function waitRecordsFunc; + switch (waitWhat) { + case INSERT_LIST: + if (countExpect == 0 || cumulatedInsertList_.size() >= countExpect) { + return; + } + // Change count has not reach countExpect + waitRecordsFunc = [this, countExpect]()->bool { + MST_LOG("[NbObserver][Wait] #### this->cumulatedInsertList_.size()=%zu, countExpect=%d ####", + this->cumulatedInsertList_.size(), countExpect); + return this->cumulatedInsertList_.size() >= countExpect; + }; + break; + case UPDATE_LIST: + if (countExpect == 0 || cumulatedUpdateList_.size() >= countExpect) { + return; + } + // Change count has not reach countExpect + waitRecordsFunc = [this, countExpect]()->bool { + MST_LOG("[NbObserver][Wait] #### this->cumulatedUpdateList_.size()=%zu, countExpect=%d ####", + this->cumulatedUpdateList_.size(), countExpect); + return this->cumulatedUpdateList_.size() >= countExpect; + }; + break; + case DELETE_LIST: + if (countExpect == 0 || cumulatedDeleteList_.size() >= countExpect) { + return; + } + // Change count has not reach countExpect + waitRecordsFunc = [this, countExpect]()->bool { + MST_LOG("[NbObserver][Wait] #### this->cumulatedDeleteList_.size()=%zu, countExpect=%d ####", + this->cumulatedDeleteList_.size(), countExpect); + return this->cumulatedDeleteList_.size() >= countExpect; + }; + break; + default: + break; + } + + MST_LOG("###########################- BEGIN -###########################"); + std::unique_lock waitRecordsLock(waitChangeMutex_); + if (timeout == 0) { + waitChangeCv_.wait(waitRecordsLock, waitRecordsFunc); + } else { + waitChangeCv_.wait_for(waitRecordsLock, std::chrono::seconds(timeout), waitRecordsFunc); + } + + MST_LOG("############################ E-N-D ############################"); +} + +microClock_type KvStoreObserverImpl::GetOnChangeTime() +{ + return onChangeTime_; +} + +void KvStoreObserverImpl::Clear() +{ + insertedEntries_.clear(); + updatedEntries_.clear(); + deleteEntries_.clear(); + changed_ = 0; + cumulatedInsertList_.clear(); + cumulatedUpdateList_.clear(); + cumulatedDeleteList_.clear(); +} + +void KvStoreObserverImpl::SetCumulatedFlag(bool isSaveCumulatedData) +{ + isSaveCumulatedData_ = isSaveCumulatedData; +} + +bool KvStoreObserverImpl::GetCumulatedFlag() const +{ + return isSaveCumulatedData_; +} + +const std::list KvStoreObserverImpl::GetCumulatedInsertList() const +{ + return cumulatedInsertList_; +} + +const std::list KvStoreObserverImpl::GetCumulatedUpdateList() const +{ + return cumulatedUpdateList_; +} + +const std::list KvStoreObserverImpl::GetCumulatedDeleteList() const +{ + return cumulatedDeleteList_; +} + +bool VerifyObserverResult(const KvStoreObserverImpl &pObserver, int changedTimes, ListType type, const list &lst) +{ + if (pObserver.GetCumulatedFlag()) { + int expectListSize = lst.size(); + pObserver.WaitUntilReachRecordCount(expectListSize, type); + } else { + pObserver.WaitUntilReachChangeCount(changedTimes); + } + + list retLst; + if (pObserver.GetCumulatedFlag()) { + if (type == INSERT_LIST) { + MST_LOG("type == INSERT_LIST"); + retLst = pObserver.GetCumulatedInsertList(); + } else if (type == UPDATE_LIST) { + MST_LOG("type == UPDATE_LIST"); + retLst = pObserver.GetCumulatedUpdateList(); + } else { + MST_LOG("type == DELETE_LIST"); + retLst = pObserver.GetCumulatedDeleteList(); + } + } else { + if (type == INSERT_LIST) { + MST_LOG("type == INSERT_LIST"); + retLst = pObserver.GetInsertList(); + } else if (type == UPDATE_LIST) { + MST_LOG("type == UPDATE_LIST"); + retLst = pObserver.GetUpdateList(); + } else { + MST_LOG("type == DELETE_LIST"); + retLst = pObserver.GetDeleteList(); + } + } + + bool result = true; + result = CompareList(retLst, lst); + MST_LOG("VerifyObserverResult CompareList result is %d", result); + return result; +} + +bool VerifyObserverResult(const KvStoreObserverImpl &pObserver, int changedTimes, ListType type, + const vector &vec) +{ + list entriesList; + for (const auto &entry : vec) { + entriesList.push_back(entry); + } + return VerifyObserverResult(pObserver, changedTimes, type, entriesList); +} + +void KvStoreSnapshotCallback::Callback(DBStatus status, const std::vector &entriesVec) +{ + this->status_ = status; + this->entriesVec_ = entriesVec; + MST_LOG("KvStoreSnapshotCallback status: %d, entriesVec.size(): %zu", status, entriesVec.size()); +} + +DBStatus KvStoreSnapshotCallback::GetStatus() +{ + return status_; +} + +std::vector KvStoreSnapshotCallback::GetEntries() +{ + return entriesVec_; +} + +void AutoLaunchCallback::AutoLaunchNotifier(const std::string &userId, const std::string &appId, + const std::string &storeId, AutoLaunchStatus status) +{ + MST_LOG("The db: %s,%s,%s is set to sync by closed status and comes some data changes now.", userId.c_str(), + appId.c_str(), storeId.c_str()); + this->realStatus_ = status; +} + +bool AutoLaunchCallback::AutoLaunchRequestNotifier(const std::string &identifier, AutoLaunchParam ¶m) +{ + auto iter = find(hashIdentities_.begin(), hashIdentities_.end(), identifier); + if (iter != hashIdentities_.end()) { + param.userId = autoLaunchParam_.userId; + param.appId = autoLaunchParam_.appId; + param.storeId = autoLaunchParam_.storeId; + param.option = autoLaunchParam_.option; + param.notifier = autoLaunchParam_.notifier; + return true; + } + return false; +} + +int AutoLaunchCallback::GetStatus() +{ + return realStatus_; +} + +void AutoLaunchCallback::Clear() +{ + this->realStatus_ = 0; +} + +void AutoLaunchCallback::AddHashIdentity(const string &hashIdentity) +{ + hashIdentities_.push_back(hashIdentity); +} + +void AutoLaunchCallback::ClearHashIdentities() +{ + hashIdentities_.clear(); +} + +void AutoLaunchCallback::SetAutoLaunchParam(AutoLaunchParam &autoLaunchParam) +{ + autoLaunchParam_ = autoLaunchParam; +} \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/src/distributeddb_data_generator.cpp b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_data_generator.cpp new file mode 100755 index 000000000..291aef945 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_data_generator.cpp @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_generator.h" + +namespace DistributedDBDataGenerator { +void GenerateRecord(unsigned int keyNo, DistributedDB::Entry &entry, + std::vector keyPrefix) +{ + std::string cntStr = std::to_string(keyNo); + entry.key = keyPrefix; + entry.value = { 'v' }; + for (auto cntStrIt = cntStr.begin(); cntStrIt != cntStr.end(); ++cntStrIt) { + entry.key.push_back(*cntStrIt); + entry.value.push_back(*cntStrIt); + } +} + +void GenerateCharSet(std::vector &charSet) +{ + for (uint8_t ch = '0'; ch <= '9'; ch++) { + charSet.push_back(ch); + } + for (uint8_t ch = 'A'; ch <= 'Z'; ch++) { + charSet.push_back(ch); + } + for (uint8_t ch = 'a'; ch <= 'z'; ch++) { + charSet.push_back(ch); + } +} + +void GenerateAlphaNumUnderlineCharSet(std::vector &charSet) +{ + GenerateCharSet(charSet); + charSet.push_back('_'); +} + +void GenerateSpecialCharSet(std::vector &charSet) +{ + charSet.push_back('\\'); + charSet.push_back('/'); + charSet.push_back('&'); + charSet.push_back('^'); + charSet.push_back('%'); + charSet.push_back('#'); + charSet.push_back('-'); +} + +void GenerateFixedLenRandString(unsigned int neededLen, RandType randType, std::string &genString) +{ + genString.clear(); + std::vector charSet; + if (randType == RandType::ALPHA_NUM) { + GenerateCharSet(charSet); + } else if (randType == RandType::ALPHA_NUM_UNDERLINE) { + GenerateAlphaNumUnderlineCharSet(charSet); + } else if (randType == RandType::SPECIAL) { + GenerateSpecialCharSet(charSet); + } + + for (unsigned int index = 0; index < neededLen; ++index) { + // the randIdx range is from 0 to (charSet.length() - 1) which is the elements quantity of charSet + int randIdx = GetRandInt(0, charSet.size() - 1); + genString.push_back(charSet[randIdx]); + } +} + +void GenerateRandRecord(DistributedDB::Entry &entry, EntrySize &entrySize, unsigned int keyNo) +{ + std::string cntStr = std::to_string(keyNo); + unsigned int len = cntStr.length(); + if ((entrySize.keySize < len) || (entrySize.valSize < len)) { + MST_LOG("ERROR:The size of key or value given is too small!"); + return; + } + std::vector charSet; + GenerateCharSet(charSet); + for (unsigned int i = 0; i < entrySize.keySize - len; i++) { + int seed = GetRandInt(0, 61); // the seed range is from 0 to 61 which is the elements quantity of charSet + entry.key.push_back(charSet[seed]); + } + if (entrySize.valSize < ONE_K_LONG_STRING) { + for (unsigned int i = 0; i < entrySize.valSize - len; i++) { + int seed = GetRandInt(0, 61); // the seed range is from 0 to 61 which is the elements quantity of charSet + entry.value.assign(entrySize.valSize - len, charSet[seed]); + } + } else { + int seed = GetRandInt(0, 61); // the seed range is from 0 to 61 which is the elements quantity of charSet + entry.value.assign(entrySize.valSize - len, charSet[seed]); + } + for (auto ch = cntStr.begin(); ch != cntStr.end(); ch++) { + entry.key.push_back(*ch); + entry.value.push_back(*ch); + } +} + +void GenerateLongRecord(unsigned int keyNo, DistributedDB::Entry &entry, + const std::vector &keyPrefix) +{ + std::string cntStr = std::to_string(keyNo); + entry.key = keyPrefix; + entry.value.assign(ONE_K_LONG_STRING, 'v'); + for (auto cntStrIt = cntStr.begin(); cntStrIt != cntStr.end(); ++cntStrIt) { + entry.key.push_back(*cntStrIt); + entry.value.push_back(*cntStrIt); + } +} + +void GenerateRecords(unsigned int recordNum, unsigned int start, std::vector &allKeys, + std::vector &entriesBatch, const std::vector keyPrifix) +{ + DistributedDB::Entry entryCurrent; + for (unsigned int cnt = start; cnt < start + recordNum; ++cnt) { + GenerateRecord(cnt, entryCurrent, keyPrifix); + allKeys.push_back(entryCurrent.key); + entriesBatch.push_back(entryCurrent); + } +} + +void GenerateMaxBigRecord(unsigned int keyNo, DistributedDB::Entry &entry, + const std::vector &keyPrefix, unsigned int num) +{ + std::string cntStr = std::to_string(keyNo); + entry.key = keyPrefix; + entry.value.assign(FOUR_M_LONG_STRING, ('v' - num)); + for (auto cntStrIt = cntStr.begin(); cntStrIt != cntStr.end(); ++cntStrIt) { + entry.key.push_back(*cntStrIt); + } + std::vector keyTail; + keyTail.assign(ONE_K_LONG_STRING - entry.key.size(), ('k' + num)); + for (auto iter = keyTail.begin(); iter != keyTail.end(); ++iter) { + entry.key.push_back(*iter); + } +} + +bool GenerateMaxBigRecords(unsigned int recordNum, unsigned int start, + std::vector &allKeys, std::vector &entriesBatch) +{ + if (recordNum > (ACSIIEND - 'k')) { + MST_LOG("Record generate failed, character is over ASCII, please use other method !"); + return false; + } else { + DistributedDB::Entry entryCurrent; + for (unsigned int cnt = start; cnt < start + recordNum; ++cnt) { + GenerateMaxBigRecord(cnt, entryCurrent, K_SEARCH_3, (cnt - start)); + allKeys.push_back(entryCurrent.key); + entriesBatch.push_back(entryCurrent); + } + } + return true; +} + +void GenerateTenThousandRecords(unsigned int recordNum, unsigned int start, + std::vector &allKeys, std::vector &entriesBatch) +{ + DistributedDB::Entry entryCurrent; + for (unsigned int cnt = start; cnt < start + recordNum; ++cnt) { + GenerateLongRecord(cnt, entryCurrent, K_SEARCH_5); + allKeys.push_back(entryCurrent.key); + entriesBatch.push_back(entryCurrent); + } +} + +void GenerateNormalAsciiRecords(DistributedDB::Entry &entry) +{ + entry.key.clear(); + entry.value.clear(); + for (uint8_t lowc = 'a'; lowc <= 'z'; ++lowc) { + entry.key.push_back(lowc); + entry.value.push_back(lowc); + } + for (uint8_t bigc = 'A'; bigc <= 'Z'; ++bigc) { + entry.key.push_back(bigc); + entry.value.push_back(bigc); + } + for (uint8_t numc = '0'; numc <= '9'; ++numc) { + entry.key.push_back(numc); + entry.value.push_back(numc); + } +} + +void GenerateFullAsciiRecords(DistributedDB::Entry &entry) +{ + entry.key.clear(); + entry.value.clear(); + for (uint8_t lowc = ACSIIEND; lowc > 0; --lowc) { + entry.key.push_back(lowc); + entry.value.push_back(lowc); + } +} + +void GenerateBiggistKeyRecords(DistributedDB::Entry &entry) +{ + entry.key.clear(); + entry.value.clear(); + for (auto lowc = ONE_K_LONG_STRING; lowc > 0; --lowc) { + entry.key.push_back(lowc); + } + entry.value.push_back('v'); +} + +DistributedDB::Entry GenerateFixedLenKVRecord(unsigned int serialNo, + unsigned int keyLen, uint8_t keyFilledChr, + unsigned int valueLen, uint8_t valueFilledChr) +{ + DistributedDB::Entry entry; + std::string serialNoStr = std::to_string(serialNo); + entry.key.assign(keyLen - serialNoStr.length(), keyFilledChr); + entry.value.assign(valueLen - serialNoStr.length(), valueFilledChr); + for (unsigned int index = 0; index < serialNoStr.size(); ++index) { + entry.key.push_back(serialNoStr[index]); + entry.value.push_back(serialNoStr[index]); + } + return entry; +} + +void GenerateFixedRecords(std::vector &entries, + std::vector &allKeys, + int recordNum, unsigned int keySize, unsigned int valSize) +{ + DistributedDB::Entry entry; + for (int cnt = DEFAULT_START; cnt <= recordNum; ++cnt) { + std::string cntStr = std::to_string(cnt); + int len = cntStr.length(); + entry.key.assign((keySize - len), 'k'); + entry.value.assign((valSize - len), 'v'); + for (auto cntIt = cntStr.begin(); cntIt != cntStr.end(); ++cntIt) { + entry.key.push_back(*cntIt); + entry.value.push_back(*cntIt); + } + allKeys.push_back(entry.key); + entries.push_back(entry); + entry.key.clear(); + entry.value.clear(); + } +} + +void GenerateOneRecordForImage(int entryNo, const EntrySize &entrySize, + const std::vector &keyPrefix, const std::vector &val, DistributedDB::Entry &entry) +{ + std::vector charSet; + GenerateCharSet(charSet); + std::string ind = std::to_string(entryNo); + unsigned int len = ind.length(); + for (auto ch = IMAGE_VALUE_PRE.begin(); ch != IMAGE_VALUE_PRE.end(); ch++) { + entry.value.push_back(*ch); + } + if ((entrySize.keySize < len) || (entrySize.valSize < (len + IMAGE_VALUE_PRE.size()))) { + MST_LOG("ERROR:The size of key or value given is too small!"); + return; + } + entry.key = keyPrefix; + for (unsigned int cnt = 0; cnt < (entrySize.keySize - len - keyPrefix.size()); cnt++) { + entry.key.push_back(charSet[GetRandInt(0, 61)]); // randrom in 61 of 0-9,A-Z,a-z. + } + for (unsigned int it = 0; it < (entrySize.valSize - len - IMAGE_VALUE_PRE.size()); it++) { + entry.value.push_back(val[0]); + } + for (auto ch = ind.begin(); ch != ind.end(); ch++) { + entry.key.push_back(*ch); + entry.value.push_back(*ch); + } +} + +void GenerateRecordsForImage(std::vector &entries, EntrySize &entrySize, + int num, std::vector keyPrefix, std::vector val) +{ + for (int index = 1; index <= num; index++) { + DistributedDB::Entry entry; + GenerateOneRecordForImage(index, entrySize, keyPrefix, val, entry); + entries.push_back(entry); + } +} + +void GenerateAppointPrefixAndSizeRecord(int recordNo, const EntrySize &entrySize, + const std::vector &keyPrefix, const std::vector &valPrefix, DistributedDB::Entry &entry) +{ + std::string recNo = std::to_string(recordNo); + unsigned int len = recNo.length(); + if ((entrySize.keySize < keyPrefix.size() + len) || (entrySize.valSize < valPrefix.size() + len)) { + MST_LOG("ERROR:The size of key or value given is too small!"); + return; + } + entry.key = keyPrefix; + entry.value = valPrefix; + entry.key.insert(entry.key.end(), entrySize.keySize - keyPrefix.size() - len, '0'); + entry.value.insert(entry.value.end(), entrySize.valSize - valPrefix.size() - len, '0'); + for (auto ch = recNo.begin(); ch != recNo.end(); ch++) { + entry.key.push_back(*ch); + entry.value.push_back(*ch); + } +} + +void GenerateAppointPrefixAndSizeRecords(std::vector &entries, const EntrySize &entrySize, + int num, const std::vector &keyPrefix, const std::vector &valPrefix) +{ + entries.clear(); + DistributedDB::Entry entry; + for (int index = 1; index <= num; index++) { + GenerateAppointPrefixAndSizeRecord(index, entrySize, keyPrefix, valPrefix, entry); + entries.push_back(entry); + } +} + +int 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); +} + +void GenerateFixedLenRandRecords(std::vector &entries, + std::vector &allKeys, + int recordNum, unsigned int keySize, unsigned int valSize) +{ + entries.clear(); + allKeys.clear(); + int idx = 0; + DistributedDB::Entry entry; + std::vector charSet; + GenerateCharSet(charSet); + for (int cnt = DEFAULT_START; cnt <= recordNum; ++cnt) { + std::string cntStr = std::to_string(cnt); + int len = cntStr.length(); + entry.key.push_back('k'); + entry.value.push_back('v'); + for (unsigned int operCnt = 0; operCnt < keySize - len - 1; ++operCnt) { + idx = GetRandInt(0, 61); // the seed range is from 0 to 61 which is the elements quantity of charSet + entry.key.push_back(charSet[idx]); + } + for (unsigned int operCnt = 0; operCnt < valSize - len - 1; ++operCnt) { + idx = GetRandInt(0, 61); // the seed range is from 0 to 61 which is the elements quantity of charSet + entry.value.push_back(charSet[idx]); + } + for (auto cntIt = cntStr.begin(); cntIt != cntStr.end(); ++cntIt) { + entry.key.push_back(*cntIt); + entry.value.push_back(*cntIt); + } + allKeys.push_back(entry.key); + entries.push_back(entry); + entry.key.clear(); + entry.value.clear(); + } +} + +const std::string GetDbType(const int type) +{ + switch(type) { + case UNENCRYPTED_DISK_DB: + return std::string("UnencrpytedDiskDB"); + case ENCRYPTED_DISK_DB: + return std::string("EncrpytedDiskDB"); + case MEMORY_DB: + return std::string("MemoryDB"); + default: + return std::string("ErrorType"); + } +} + +void GenerateRandomRecords(std::vector &entries, EntrySize &entrySize, int num) +{ + for (int index = 0; index < num; index++) { + DistributedDB::Entry entry; + entry.key.resize(entrySize.keySize); + RAND_bytes(entry.key.data(), entrySize.keySize); + entry.value.resize(entrySize.valSize); + RAND_bytes(entry.value.data(), entrySize.valSize); + entries.push_back(entry); + } +} +// Get long schema define with long default x or fields' num +void GetLongSchemaDefine(LongDefine ¶m, std::string &longDefine) +{ + std::vector defaultStr; + std::string longString; + for (int index = 1; index <= param.recordNum; index++) { + std::string ind = std::to_string(index); + int len = ind.length(); + if (param.recordSize < (len + 1)) { + MST_LOG("ERROR:The size of key or value given is too small!"); + return; + } + longString.assign(param.recordSize - len, param.prefix); + longString.append(ind); + defaultStr.push_back(longString); + longString.clear(); + } + longDefine.append("{"); + for (int index = 0; index < param.recordNum; index++) { + longDefine = longDefine + "\"field" + std::to_string(index) + "\":" + "\"STRING,NOT NULL,DEFAULT " + \ + "'" + defaultStr[index] + "'\","; + } + longDefine.erase(longDefine.size() - 1, 1); + longDefine.append("}"); + MST_LOG("longDefine.size() is %zu", longDefine.size()); +} +// splice different string to schema +const std::string SpliceToSchema(const std::string &version, const std::string &mode, + const std::string &define, const std::string &index, const std::string &skipSize) +{ + std::string schema; + std::string middleString; + if (index.empty() && skipSize.empty()) { + middleString = ""; + } else if (!index.empty() && skipSize.empty()) { + middleString = middleString + ",\"SCHEMA_INDEXES\":" + index; + } else if (index.empty() && !skipSize.empty()) { + middleString = middleString + ",\"SCHEMA_SKIPSIZE\":" + skipSize; + } else { + middleString = middleString + ",\"SCHEMA_INDEXES\":" + index + "," + "\"SCHEMA_SKIPSIZE\":" + skipSize; + } + schema = schema + "{" + "\"SCHEMA_VERSION\"" + ":" + "\"" + version + "\"" + "," + + "\"SCHEMA_MODE\":" + "\"" + mode + "\"" + "," + + "\"SCHEMA_DEFINE\"" + ":" + define + middleString + "}"; + return schema; +} +// the size of field is 64B, of DEFAULT x is 4K, all of them is valid +void GenerateLongValidSchema(Schema &validSchema, std::vector &schema) +{ + std::string validLongSchema; + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = FOUR_K_LONG_STRING; + param.prefix = 'k'; + GetLongSchemaDefine(param, validLongSchema); + validLongSchema.replace(2, 6, KEY_SIXTYFOUR_BYTE, 'a'); // the 6 str starting at 2 is being replaced. + std::string splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), + validLongSchema, validSchema.index.at(0)); + schema.push_back(splicSchema); +} +// the num of field is 257 with repeat field name, the num of index is 32, but it is valid +void GenerateLargeValidSchema(Schema &validSchema, std::vector &schema) +{ + std::string validLargeSchema; + LongDefine param; + param.recordNum = TWO_FIVE_SIX_RECORDS; + param.recordSize = KEY_SIX_BYTE; + param.prefix = 'k'; + GetLongSchemaDefine(param, validLargeSchema); + validLargeSchema.erase(validLargeSchema.size() - 1, 1); + validLargeSchema.append(",\"field0\":\"STRING,NOT NULL,DEFAULT 'kkkkk1'\"}"); + std::string splicSchema, largeIndexRes, largeIndex; + for (int index = 0; index < KEY_THIRTYTWO_BYTE; index++) { + largeIndexRes = largeIndexRes + "\"$.field" + std::to_string(index) + "\","; + } + largeIndexRes.erase(largeIndexRes.size() - 1, 1); + largeIndex = largeIndex + "[" + largeIndexRes + "]"; + splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), + validLargeSchema, largeIndex); + schema.push_back(splicSchema); +} + +std::vector GetValidSchema(Schema &validSchema, bool hasIndex) +{ + std::vector schema; + for (auto iter1 = validSchema.version.begin(); iter1 != validSchema.version.end(); iter1++) { + for (auto iter2 = validSchema.mode.begin(); iter2 != validSchema.mode.end(); iter2++) { + std::string splicSchema; + if (hasIndex) { + for (auto iter3 = validSchema.index.begin(); iter3 != validSchema.index.end(); iter3++) { + splicSchema = SpliceToSchema(*iter1, *iter2, validSchema.define.at(0), *iter3); + schema.push_back(splicSchema); + } + } else { + for (auto iter3 = validSchema.define.begin(); iter3 != validSchema.define.end(); iter3++) { + splicSchema = SpliceToSchema(*iter1, *iter2, *iter3, validSchema.index.at(0)); + schema.push_back(splicSchema); + } + } + } + } + GenerateLongValidSchema(validSchema, schema); + GenerateLargeValidSchema(validSchema, schema); + std::string schemaWithoutIndex; + schemaWithoutIndex = schemaWithoutIndex + "{" + "\"SCHEMA_VERSION\"" + ":" + "\"" + validSchema.version.at(0) + + "\"" + "," + "\"SCHEMA_MODE\"" + ":" + "\"" + validSchema.mode.at(0) + "\"" + "," + + "\"SCHEMA_DEFINE\"" + ":" + validSchema.define.at(0) + "}"; + schema.push_back(schemaWithoutIndex); + MST_LOG("The number of valid schema is %zd", schema.size()); + return schema; +} + +void GetLongIndex(Schema &validSchema, std::vector &schema) +{ + std::string validLargeSchema, largeIndexRes, largeIndex, splicSchema; + LongDefine param; + param.recordNum = FIFTY_RECORDS; + param.recordSize = KEY_SIX_BYTE; + param.prefix = 'k'; + GetLongSchemaDefine(param, validLargeSchema); + for (int index = 0; index <= KEY_THIRTYTWO_BYTE; index++) { + largeIndexRes = largeIndexRes + "\"$.field" + std::to_string(index) + "\","; + } + largeIndexRes.erase(largeIndexRes.size() - 1, 1); + largeIndex = largeIndex + "[" + largeIndexRes + "]"; + splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), + validLargeSchema, largeIndex); + schema.push_back(splicSchema); +} +void GenarateOtherInvalidSchema(Schema &validSchema, std::map> &result) +{ + // exist no Metafield or lack of Metafield + std::string invalidSchema; + std::vector schema; + invalidSchema = invalidSchema + "{" + "\"SCHEMA_VERSION\"" + ":" + "\"" + validSchema.version.at(0) + + "\"" + "," + "\"SCHEMA_MODE\"" + ":" + "\"" + validSchema.mode.at(0) + "\"" + "," + + "\"SCHEMA_DEFINE\"" + ":" + validSchema.define.at(0) + "," + + "\"SCHEMA_NOTE\"" + ":" + "[]" + "}"; + schema.push_back(invalidSchema); + invalidSchema.clear(); + invalidSchema = invalidSchema + "{" + "\"SCHEMA_VERSION\"" + ":" + "\"" + validSchema.version.at(0) + + "\"" + "," + "\"SCHEMA_MODE\"" + ":" + "\"" + validSchema.mode.at(0) + "\"" + "}"; + schema.push_back(invalidSchema); + + // the schema is invalid that is a Json object + invalidSchema.clear(); + invalidSchema = invalidSchema + "[" + "\"SCHEMA_VERSION\"" + ":" + "\"" + validSchema.version.at(0) + + "\"" + "," + "\"SCHEMA_MODE\"" + ":" + "\"" + validSchema.mode.at(0) + "\"" + "]"; + schema.push_back(invalidSchema); + + // if the schema is \",nullptr,space,tab or enter or other not match Json, it is invalid + schema.push_back("\""); + schema.push_back(" "); + schema.push_back("\t"); + schema.push_back("\r"); + invalidSchema.clear(); + invalidSchema = invalidSchema + "{" + "\"SCHEMA_VERSION\"" + ":" + "\"" + validSchema.version.at(0) + + "\"" + "," + "\"SCHEMA_MODE\"" + ":" + "\"" + validSchema.mode.at(0) + "\"" + "," + + "\"SCHEMA_DEFINE\"" + ":" + validSchema.define.at(0) + "," + "}"; + schema.push_back(invalidSchema); + + // if the schema's size is over 512K, it is invalid + invalidSchema.clear(); + LongDefine param; + param.recordNum = TWO_FIVE_SIX_RECORDS; + param.recordSize = TWO_K_LONG_STRING; + param.prefix = 'k'; + GetLongSchemaDefine(param, invalidSchema); + std::string splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), + invalidSchema, validSchema.index.at(0)); + schema.push_back(splicSchema); + + // if the num of index is over 32, it is invalid + GetLongIndex(validSchema, schema); + result[4] = schema; // this invalid scenes' index is 4. +} + +std::map> GetInvalidSchema(Schema &invalidSchema, Schema &validSchema, bool hasIndex) +{ + std::map> result; + std::vector schema; + std::string splicSchema; + for (auto iter = invalidSchema.version.begin(); iter != invalidSchema.version.end(); iter++) { + splicSchema = SpliceToSchema(*iter, validSchema.mode.at(0), validSchema.define.at(0), validSchema.index.at(0)); + schema.push_back(splicSchema); + } + result[0] = schema; + schema.clear(); + for (auto iter = invalidSchema.mode.begin(); iter != invalidSchema.mode.end(); iter++) { + splicSchema = SpliceToSchema(validSchema.version.at(0), *iter, validSchema.define.at(0), + validSchema.index.at(0)); + schema.push_back(splicSchema); + } + result[1] = schema; + schema.clear(); + if (hasIndex) { + for (auto iter = invalidSchema.index.begin(); iter != invalidSchema.index.end(); iter++) { + splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), + validSchema.define.at(0), *iter); + schema.push_back(splicSchema); + } + result[3] = schema; // 3 is the invalid SCHEMA_INDEX. + } else { + for (auto iter = invalidSchema.define.begin(); iter != invalidSchema.define.end(); iter++) { + splicSchema = SpliceToSchema(validSchema.version.at(0), validSchema.mode.at(0), *iter, "[]"); + schema.push_back(splicSchema); + } + result[2] = schema; // 2 is the invalid SCHEMA_DEFINE. + } + GenarateOtherInvalidSchema(validSchema, result); + MST_LOG("The number of invalid schema is %zd", result[0].size() + result[1].size() + + result[2].size() + result[3].size() + result[4].size()); // 1, 2, 3, 4 are the index of invalid field. + return result; +} +} // end of namespace DistributedDBDataGenerator \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/src/distributeddb_nb_test_tools.cpp b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_nb_test_tools.cpp new file mode 100755 index 000000000..d929793b9 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_nb_test_tools.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 "distributeddb_nb_test_tools.h" +#include +#include "sqlite3.h" +using namespace std; +using namespace chrono; +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +void DelegateMgrNbCallback::Callback(DBStatus status, KvStoreNbDelegate *kvStoreNbDelegate) +{ + this->status_ = status; + this->kvStoreNbDelegate_ = kvStoreNbDelegate; + MST_LOG("DelegateMgrNbCallback status:%d, kvStoreNbDelegate_null: %d", status, (kvStoreNbDelegate == nullptr)); +} + +DBStatus DelegateMgrNbCallback::GetStatus() +{ + return status_; +} + +KvStoreNbDelegate *DelegateMgrNbCallback::GetKvStore() +{ + return kvStoreNbDelegate_; +} + +KvStoreNbDelegate* DistributedDBNbTestTools::GetNbDelegateSuccess(KvStoreDelegateManager *&outManager, + const DBParameters ¶m, const Option &optionParam) +{ + MST_LOG("GetNbDelegate isMemoryDb= %d, isEncryptedDb= %d", optionParam.isMemoryDb, + optionParam.isEncryptedDb); + SetDir(NB_DIRECTOR); + if (param.storeId.empty() || param.appId.empty() || param.userId.empty()) { + return nullptr; + } + + // define a Callback to hold the KvStoreNbDelegate and status. + DelegateMgrNbCallback delegateMgrCallback; + function function + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + if (outManager != nullptr) { + delete outManager; + outManager = nullptr; + } + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(param.appId, param.userId); + if (manager == nullptr) { + return nullptr; + } + DBStatus status = manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return nullptr; + } + + KvStoreNbDelegate::Option option = TransferNbOptionType(optionParam); + // get kv store, then the Callback will save the status and delegate. + manager->GetKvStore(param.storeId, option, function); + status = delegateMgrCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), status); + delete manager; + manager = nullptr; + return nullptr; + } + const KvStoreNbDelegate* delegate = const_cast(delegateMgrCallback.GetKvStore()); + if (delegate == nullptr) { + MST_LOG("%s GetKvStore failed! delegate nullptr.", TAG.c_str()); + delete manager; + manager = nullptr; + return nullptr; + } + + MST_LOG("%s GetKvStore success: %s %s %s %d", TAG.c_str(), + param.storeId.c_str(), param.appId.c_str(), param.userId.c_str(), option.createIfNecessary); + outManager = manager; + return const_cast(delegate); +} + +KvStoreNbDelegate* DistributedDBNbTestTools::GetNbDelegateStatus(KvStoreDelegateManager *&outManager, + DBStatus &statusReturn, const DBParameters ¶m, const Option &optionParam) +{ + MST_LOG("GetNbDelegate isMemoryDb= %d, isEncryptedDb= %d", optionParam.isMemoryDb, + optionParam.isEncryptedDb); + SetDir(NB_DIRECTOR); + if (param.storeId.empty() || param.appId.empty() || param.userId.empty()) { + return nullptr; + } + + // define a Callback to hold the KvStoreNbDelegate and status. + DelegateMgrNbCallback delegateMgrCallback; + function function + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + if (outManager != nullptr) { + delete outManager; + outManager = nullptr; + } + KvStoreDelegateManager *manager1 = new (std::nothrow) KvStoreDelegateManager(param.appId, param.userId); + if (manager1 == nullptr) { + return nullptr; + } + DBStatus status = manager1->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + delete manager1; + manager1 = nullptr; + return nullptr; + } + + KvStoreNbDelegate::Option option = TransferNbOptionType(optionParam); + // get kv store, then the Callback will save the status and delegate. + manager1->GetKvStore(param.storeId, option, function); + status = delegateMgrCallback.GetStatus(); + statusReturn = status; + if (status != DBStatus::OK) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), status); + delete manager1; + manager1 = nullptr; + return nullptr; + } + const KvStoreNbDelegate* delegate = const_cast(delegateMgrCallback.GetKvStore()); + if (delegate == nullptr) { + MST_LOG("%s GetKvStore failed! delegate nullptr.", TAG.c_str()); + delete manager1; + manager1 = nullptr; + return nullptr; + } + + MST_LOG("%s GetKvStore success: %s %s %s %d", TAG.c_str(), + param.storeId.c_str(), param.appId.c_str(), param.userId.c_str(), option.createIfNecessary); + outManager = manager1; + return const_cast(delegate); +} + +DBStatus DistributedDBNbTestTools::GetNbDelegateStoresSuccess(KvStoreDelegateManager *&outManager, + vector &outDelegateVec, + const vector &storeIds, const string &appId, const string &userId, const Option &optionParam) +{ + SetDir(NB_DIRECTOR); + unsigned long opCnt; + DBStatus status = DBStatus::OK; + for (opCnt = 0; opCnt < storeIds.size(); ++opCnt) { + if (storeIds[opCnt].empty() || appId.empty() || userId.empty()) { + return INVALID_ARGS; + } + } + + // use appid and userid to initialize a kvStoreDelegateManager, and set the default cfg. + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(appId, userId); + if (manager == nullptr) { + return DBStatus::DB_ERROR; + } + outDelegateVec.clear(); + KvStoreNbDelegate::Option option = TransferNbOptionType(optionParam); + for (opCnt = 0; opCnt < storeIds.size(); ++opCnt) { + // define a Callback to hold the KvStoreNbDelegate and status. + DelegateMgrNbCallback delegateMgrCallback; + function function + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + + status = manager->SetKvStoreConfig(CONFIG); + if (status != DBStatus::OK) { + MST_LOG("%s SetConfig failed! Status= %d", TAG.c_str(), status); + goto END; + } + + // get kv store, then the Callback will save the status and delegate. + manager->GetKvStore(storeIds[opCnt], option, function); + status = delegateMgrCallback.GetStatus(); + if (status != DBStatus::OK) { + MST_LOG("%s GetKvStore failed! Status= %d", TAG.c_str(), status); + goto END; + } + const KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + if (delegate == nullptr) { + MST_LOG("%s GetKvStore failed! delegate nullptr.", TAG.c_str()); + goto END; + } + outDelegateVec.push_back(const_cast(delegate)); + } + outManager = manager; + return OK; +END: + delete manager; + manager = nullptr; + return status; +} + +KvStoreNbDelegate::Option DistributedDBNbTestTools::TransferNbOptionType(const Option &optionParam) +{ + KvStoreNbDelegate::Option option; + option.createIfNecessary = optionParam.createIfNecessary; + option.isMemoryDb = optionParam.isMemoryDb; + option.isEncryptedDb = optionParam.isEncryptedDb; + option.cipher = optionParam.cipher; + option.schema = optionParam.schema; + (void)option.passwd.SetValue(optionParam.passwd.data(), optionParam.passwd.size()); + return option; +} + +// this static method is to compare if the two Value has the same data. +bool DistributedDBNbTestTools::isValueEquals(const DistributedDB::Value &v1, const DistributedDB::Value &v2) +{ + // just return false if the sizes are not the same. + if (v1.size() != v2.size()) { + return false; + } + + // compare two Values char by char. + return v1 == v2; +} + +DistributedDB::DBStatus DistributedDBNbTestTools::Get(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, DistributedDB::Value &value) +{ + return kvStoreNbDelegate.Get(key, value); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::GetEntries(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &keyPrefix, std::vector &entries) +{ + return kvStoreNbDelegate.GetEntries(keyPrefix, entries); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::Put(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, const DistributedDB::Value &value) +{ + return kvStoreNbDelegate.Put(key, value); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::PutBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &entries) +{ + DistributedDB::DBStatus status; + unsigned int cnt = entries.size(); + int index = 0; + while (cnt > BATCH_RECORDS) { + cnt -= BATCH_RECORDS; + std::vector entriesBatch(entries.begin() + index * BATCH_RECORDS, + entries.begin() + (index + 1) * BATCH_RECORDS); + status = kvStoreNbDelegate.PutBatch(entriesBatch); + index++; + if (status != DBStatus::OK) { + return status; + } + } + std::vector entriesBatch(entries.begin() + index * BATCH_RECORDS, + entries.end()); + status = kvStoreNbDelegate.PutBatch(entriesBatch); + + return status; +} + +DistributedDB::DBStatus DistributedDBNbTestTools::Delete(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key) +{ + return kvStoreNbDelegate.Delete(key); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::DeleteBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &keys) +{ + int cnt = 0; + std::vector keysBatch; + DistributedDB::DBStatus status; + for (const auto &iter : keys) { + keysBatch.push_back(iter); + cnt++; + if (cnt % BATCH_RECORDS == 0 || cnt == static_cast(keys.size())) { + status = kvStoreNbDelegate.DeleteBatch(keysBatch); + if (status != DBStatus::OK) { + return status; + } + keysBatch.clear(); + } + } + return DBStatus::OK; +} + +DistributedDB::DBStatus DistributedDBNbTestTools::GetLocal(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, DistributedDB::Value &value) +{ + return kvStoreNbDelegate.GetLocal(key, value); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::PutLocal(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, const DistributedDB::Value &value) +{ + return kvStoreNbDelegate.PutLocal(key, value); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::PutLocalBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &entries) +{ + int cnt = 0; + std::vector entriesBatch; + DistributedDB::DBStatus status; + for (const auto &iter : entries) { + entriesBatch.push_back(iter); + cnt++; + if (cnt % BATCH_RECORDS == 0 || cnt == static_cast(entries.size())) { + status = kvStoreNbDelegate.PutLocalBatch(entriesBatch); + if (status != DBStatus::OK) { + return status; + } + entriesBatch.clear(); + } + } + return DBStatus::OK; +} + +DistributedDB::DBStatus DistributedDBNbTestTools::DeleteLocal( + DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, const DistributedDB::Key &key) +{ + return kvStoreNbDelegate.DeleteLocal(key); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::DeleteLocalBatch(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const std::vector &keys) +{ + int cnt = 0; + std::vector keysBatch; + DistributedDB::DBStatus status; + for (const auto &iter : keys) { + keysBatch.push_back(iter); + cnt++; + if (cnt % BATCH_RECORDS == 0 || cnt == static_cast(keys.size())) { + status = kvStoreNbDelegate.DeleteLocalBatch(keysBatch); + if (status != DBStatus::OK) { + return status; + } + keysBatch.clear(); + } + } + return DBStatus::OK; +} + +DistributedDB::DBStatus DistributedDBNbTestTools::RegisterObserver(DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, + const DistributedDB::Key &key, unsigned int mode, DistributedDB::KvStoreObserver *observer) +{ + return kvStoreNbDelegate.RegisterObserver(key, mode, observer); +} + +DistributedDB::DBStatus DistributedDBNbTestTools::UnRegisterObserver( + DistributedDB::KvStoreNbDelegate &kvStoreNbDelegate, const DistributedDB::KvStoreObserver *observer) +{ + return kvStoreNbDelegate.UnRegisterObserver(observer); +} + +bool DistributedDBNbTestTools::CloseNbAndRelease(KvStoreDelegateManager *&manager, KvStoreNbDelegate *&delegate) +{ + bool result = true; + if (delegate != nullptr && manager != nullptr) { + result = (manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + } else { + MST_LOG("Close Failed"); + return false; + } + return result; +} + +bool EndCaseDeleteDB(DistributedDB::KvStoreDelegateManager *&manager, + DistributedDB::KvStoreNbDelegate *&nbDelegate, const std::string base, bool isMemoryDb) +{ + bool isResult = true; + isResult = (manager->CloseKvStore(nbDelegate) == OK); + MST_LOG("CloseKvStore result:%d", isResult); + nbDelegate = nullptr; + + if (!isMemoryDb) { + isResult = isResult && (manager->DeleteKvStore(base) == OK); + } + MST_LOG("DeleteKvStore result:%d", isResult); + delete manager; + manager = nullptr; + return isResult; +} + +void ConflictNbCallback::NotifyCallBack(const DistributedDB::KvStoreNbConflictData &data, + std::vector *&conflictData) +{ + MST_LOG("[ConflictCallback] Calling CallBack..."); + Key key; + Value oldValue; + Value newValue; + data.GetKey(key); + data.GetValue(KvStoreNbConflictData::ValueType::OLD_VALUE, oldValue); + data.GetValue(KvStoreNbConflictData::ValueType::NEW_VALUE, newValue); + conflictData->push_back({data.GetType(), key, oldValue, newValue, + data.IsDeleted(KvStoreNbConflictData::ValueType::OLD_VALUE), + data.IsDeleted(KvStoreNbConflictData::ValueType::NEW_VALUE), + data.IsNative(KvStoreNbConflictData::ValueType::OLD_VALUE), + data.IsNative(KvStoreNbConflictData::ValueType::NEW_VALUE)}); +} + +bool DistributedDBNbTestTools::ModifyDatabaseFile(const std::string &fileDir) +{ + MST_LOG("Modify file:%s", fileDir.c_str()); + std::fstream dataFile(fileDir, std::fstream::binary | std::fstream::out | std::fstream::in); + if (!dataFile.is_open()) { + MST_LOG("Open the database file failed"); + return false; + } + + if (!dataFile.seekg(0, std::fstream::end)) { + return false; + } + + uint64_t fileSize; + std::ios::pos_type pos = dataFile.tellg(); + if (pos < 0) { + return false; + } else { + fileSize = static_cast(pos); + if (fileSize < 1024) { // the least page size is 1024 bytes. + MST_LOG("Invalid database file:%lld.", static_cast(fileSize)); + return false; + } + } + + uint32_t currentCount = 0x1F1F1F1F; // add the random value to corrupt the head. + if (!dataFile.seekp(0)) { + return false; + } + for (int i = 0; i < 256; i++) { // 256 is 1024 / 4. + if (!dataFile.write(reinterpret_cast(¤tCount), sizeof(uint32_t))) { + return false; + } + } + dataFile.flush(); + dataFile.close(); + return true; +} +std::string DistributedDBNbTestTools::GetKvNbStoreDirectory(const DBParameters ¶m) +{ + std::string identifier = param.userId + "-" + param.appId + "-" + param.storeId; + std::string identifierName = TransferStringToHashHexString(identifier); + std::string filePath = NB_DIRECTOR + "/" + identifierName + "/single_ver/main/gen_natural_store.db"; + return filePath; +} +bool DistributedDBNbTestTools::MoveToNextFromBegin(KvStoreResultSet &resultSet, + const vector &entries, int recordCnt) +{ + bool result = (static_cast(entries.size()) >= recordCnt); + if (!result) { + MST_LOG("entries.size()) < recordCnt!!!"); + return result; + } + Entry entry; + int positionGot; + for (int position = -1; position < recordCnt; ++position) { // the first pos after getentries is -1. + bool expectRes = resultSet.MoveToNext(); + if (position < (recordCnt - 1)) { + result = result & expectRes; + } else { + result = result & (!expectRes); + } + if (!result) { + MST_LOG("resultSet.MoveToNext() doesn't meet expectations!!!"); + break; + } + positionGot = position + 1; + result = result & (resultSet.GetPosition() == positionGot); + if (!result) { + MST_LOG("resultSet.GetPosition() != positionGot!!!"); + break; + } + if (position < (recordCnt - 1)) { + result = result & (resultSet.GetEntry(entry) == OK); + if (!result) { + MST_LOG("resultSet.GetEntry() != OK"); + break; + } + result = result & (entry.key == entries[positionGot].key) & (entry.value == entries[positionGot].value); + if (!result) { + MST_LOG("entry != entries[positionGot]"); + break; + } + } else { + result = result & (resultSet.GetEntry(entry) == NOT_FOUND); + } + } + return result; +} + +void KvStoreNbCorruptInfo::CorruptNewCallBack(const std::string &appId, const std::string &userId, + const std::string &storeId, DistributedDB::KvStoreDelegateManager *&manager, CallBackParam &pathResult) +{ + MST_LOG("Begin to recover the corrupt DB."); + manager->DeleteKvStore(storeId); + delete manager; + manager = nullptr; + DBParameters param(storeId, appId, userId); + KvStoreNbDelegate *delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, param, g_option); + if (manager == nullptr || delegate == nullptr) { + MST_LOG("[KvStoreNbCorruptInfo::CorruptNewCallBack] Get delegate or manager failed!"); + pathResult.result = false; + } else { + if (delegate->Import(pathResult.path, NULL_PASSWD) != OK) { + MST_LOG("[KvStoreNbCorruptInfo::CorruptNewCallBack] Import failed!"); + pathResult.result = false; + } + if (manager->CloseKvStore(delegate) != OK) { + MST_LOG("[KvStoreNbCorruptInfo::CorruptNewCallBack] CloseKvStore(delegate) failed!"); + pathResult.result = false; + } + } + delegate = nullptr; + delete manager; + manager = nullptr; +} + +void KvStoreNbCorruptInfo::CorruptCallBackOfImport(const std::string &appId, const std::string &userId, + const std::string &storeId, DistributedDB::KvStoreNbDelegate *&delegate, CallBackParam &pathResult) +{ + MST_LOG("Begin to Import the recover db files."); + MST_LOG("The corrupt Db is %s, %s, %s", appId.c_str(), userId.c_str(), storeId.c_str()); + CipherPassword password; + (void)password.SetValue(FILE_PASSWD_VECTOR_1.data(), FILE_PASSWD_VECTOR_1.size()); + if (delegate->Import(pathResult.path, password) != OK) { + MST_LOG("[KvStoreNbCorruptInfo::CorruptCallBackOfImport] Import failed!"); + pathResult.result = false; + } +} + +void KvStoreNbCorruptInfo::CorruptCallBackOfExport(const std::string &appId, const std::string &userId, + const std::string &storeId, DistributedDB::KvStoreNbDelegate *&delegate, CallBackParam &pathResult) +{ + MST_LOG("Begin to Export the DB data."); + if (delegate->Export(pathResult.path, NULL_PASSWD) != INVALID_PASSWD_OR_CORRUPTED_DB) { + MST_LOG("[KvStoreNbCorruptInfo::CorruptCallBackOfExport] Export failed!"); + pathResult.result = false; + } +} \ No newline at end of file diff --git a/services/distributeddataservice/test/common/distributeddb/src/distributeddb_schema_test_tools.cpp b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_schema_test_tools.cpp new file mode 100755 index 000000000..5ebac8e63 --- /dev/null +++ b/services/distributeddataservice/test/common/distributeddb/src/distributeddb_schema_test_tools.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_schema_test_tools.h" + +#include +#include +#include "distributeddb_nb_test_tools.h" + +using namespace std; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +DistributedDB::Entry DistributedDBSchemaTestTools::GenerateFixedLenSchemaRecord(const unsigned long serialNo, + const EntrySize &entrySize, const uint8_t keyFilledChr, const uint8_t valueFilledChr) +{ + DistributedDB::Entry entry; + std::string serialNoStr = std::to_string(serialNo); + entry.key.assign(entrySize.keySize - serialNoStr.length(), keyFilledChr); + for (unsigned long index = 0; index < serialNoStr.size(); ++index) { + entry.key.push_back(serialNoStr[index]); + } + + string val = string("{") + "\"field" + std::to_string(FIRST_FIELD) + "\":" + std::to_string(serialNo) + ","; + for (unsigned int fieldIndex = SECOND_FIELD; fieldIndex <= THIRTIETH_FIELD; fieldIndex++) { + if (fieldIndex == SIXTH_FIELD) { + val += "\"field" + std::to_string(fieldIndex) + "\":" + to_string(static_cast(serialNo)) + ","; + } else { + val += "\"field" + std::to_string(fieldIndex) + "\":" + + "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + std::to_string(serialNo) + "\","; + } + } + val.back() = '}'; + size_t found = val.rfind("PerfTest"); + if (found != string::npos) { + string insert = "PerfTest"; + insert += string(entrySize.valSize - val.length(), valueFilledChr); + val.replace(found, strlen("PerfTest"), insert); + } + string scmVal = "a" + val; + DistributedDB::Value schemaVal(scmVal.begin(), scmVal.end()); + entry.value = schemaVal; + return entry; +} + +vector DistributedDBSchemaTestTools::GenerateFixedSchemaRecords( + vector &allKeys, const int recordNum, const EntrySize &entrySize, + const uint8_t keyFilledChr, const uint8_t valueFilledChr) +{ + vector entries; + DistributedDB::Entry entry; + for (int serialNo = TEST_START_CNT; serialNo <= recordNum; ++serialNo) { + entry = GenerateFixedLenSchemaRecord(serialNo, entrySize, keyFilledChr, valueFilledChr); + allKeys.push_back(entry.key); + entries.push_back(entry); + entry.key.clear(); + entry.value.clear(); + } + return entries; +} + +DistributedDB::Entry DistributedDBSchemaTestTools::GenerateFixedLenSchemaPerfRecord( + const uint64_t presetRecordsCnt, const uint64_t serialNo, const RecordInfo &recordInfo, + const string &valueSkipString) +{ + DistributedDB::Entry entry; + std::string serialNoStr = std::to_string(serialNo); + entry.key.assign(recordInfo.keyLength - serialNoStr.length(), recordInfo.keyFilledChr); + for (unsigned long index = 0; index < serialNoStr.size(); ++index) { + entry.key.push_back(serialNoStr[index]); + } + + string val = string("{") + "\"field" + std::to_string(FIRST_FIELD) + "\":" + std::to_string(serialNo) + ","; + unsigned int fieldIndex; + for (fieldIndex = SECOND_FIELD; fieldIndex <= THIRD_FIELD; fieldIndex++) { + val += "\"field" + std::to_string(fieldIndex) + "\":" + + "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + serialNoStr + "\","; + } + if (serialNo <= static_cast(presetRecordsCnt) / 2) { // 2 is an half of records. + for (fieldIndex = FOURTH_FIELD; fieldIndex <= FIFTH_FIELD; fieldIndex++) { + val += "\"field" + std::to_string(fieldIndex) + "\":" + + "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + serialNoStr + "\","; + } + } + for (; fieldIndex <= THIRTIETH_FIELD; fieldIndex++) { + if (fieldIndex == SIXTH_FIELD) { + val += "\"field" + std::to_string(fieldIndex) + "\":" + to_string(static_cast(serialNo)) + ","; + } else { + val += "\"field" + std::to_string(fieldIndex) + "\":" + + "\"SchemaPerfTest" + std::to_string(fieldIndex) + "\","; + } + } + val.back() = '}'; + size_t found = val.rfind("PerfTest"); + if (found != string::npos) { + string insert = "PerfTest"; + if (recordInfo.valueLength > val.length()) { + insert += string(recordInfo.valueLength - val.length(), recordInfo.valueFilledChr); + val.replace(found, strlen("PerfTest"), insert); + } else { + MST_LOG("[GenerateFixedLenSchemaPerfRecord] recordInfo.valueLength(%u) is too short, " \ + "it should be more than %zu.", recordInfo.valueLength, val.length()); + return entry; + } + } + string scmVal = valueSkipString + val; + DistributedDB::Value schemaVal(scmVal.begin(), scmVal.end()); + entry.value = schemaVal; + return entry; +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributed_crud_transaction_tools.h b/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributed_crud_transaction_tools.h new file mode 100755 index 000000000..4e74a0c20 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributed_crud_transaction_tools.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 DISTRIBUTED_DB_MODULE_CRUD_TRANSACTION_TOOLS_H +#define DISTRIBUTED_DB_MODULE_CRUD_TRANSACTION_TOOLS_H + +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +enum class CrudMode { + PUT_BATCH = 0, + UPDATE_BATCH = 1, + DELETE_BATCH = 2, + CLEAR = 3, + PUT = 4, + DELETE = 5, +}; +class DistributedCrudTransactionTools final { +public: + + DistributedCrudTransactionTools(DistributedDB::KvStoreDelegate &delegate, + CrudMode first, CrudMode second, bool preset, bool isLocal); + ~DistributedCrudTransactionTools() {} + bool testCrudTransaction(); + + // Delete the copy and assign constructors + DistributedCrudTransactionTools(const DistributedCrudTransactionTools &distributeDBTools) = delete; + DistributedCrudTransactionTools& operator=(const DistributedCrudTransactionTools &distributeDBTools) = delete; + DistributedCrudTransactionTools(DistributedCrudTransactionTools &&distributeDBTools) = delete; + DistributedCrudTransactionTools& operator=(DistributedCrudTransactionTools &&distributeDBTools) = delete; + +private: + bool PresetValue(); + bool CheckFirst(); + bool CheckSecond(); + void Check(); + bool Action1(DistributedDB::KvStoreDelegate &delegate); + bool Action2(DistributedDB::KvStoreDelegate &delegate); + DistributedDB::KvStoreDelegate *storeDelegate_; + CrudMode firstMode_; + CrudMode secondMode_; + bool isLocal_; + bool needPresetData_; + unsigned long presetCount_ = 128; + int presetValue_ = 1; + std::vector entriesBatch_; + std::vector allKeys_; + bool firstComplete_ = false; + bool secondComplete_ = false; + bool success_ = true; +}; + +#endif // DISTRIBUTED_DB_MODULE_CRUD_TRANSACTION_TOOLS_H \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributeddb_nb_cursor_testcase.h b/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributeddb_nb_cursor_testcase.h new file mode 100755 index 000000000..5d88fb032 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/include/distributeddb_nb_cursor_testcase.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 DISTRIBUTED_NB_CURSOR_TESTCASE_H +#define DISTRIBUTED_NB_CURSOR_TESTCASE_H + +#include +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "kv_store_result_set.h" +class DistributedNbCursorTestcase final { +public: + DistributedNbCursorTestcase() {}; + ~DistributedNbCursorTestcase() {} + static void ResultSetDb001(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb002(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb003(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb004(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb005(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb006(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb007(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb008(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb009(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb010(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb011(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb012(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb013(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb014(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb015(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb016(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb017(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb018(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb019(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb020(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb021(DistributedDB::KvStoreNbDelegate *delegate, + DistributedDB::KvStoreDelegateManager *manager, bool isRowIdMode); + static void ResultSetDb022(bool isRowIdMode); + static void ResultSetDb023(bool isRowIdMode); + static void ResultSetDb024(bool isRowIdMode); + static void ResultSetDb025(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb026(DistributedDB::KvStoreNbDelegate *delegate, bool isRowIdMode); + static void ResultSetDb027(bool isRowIdMode); +}; +#endif // DISTRIBUTED_NB_CURSOR_TESTCASE_H \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/include/process_communicator_test_stub.h b/services/distributeddataservice/test/moduletest/common/distributeddb/include/process_communicator_test_stub.h new file mode 100755 index 000000000..64975bc1d --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/include/process_communicator_test_stub.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "types.h" + +namespace DistributedDB { +class ProcessCommunicatorTestStub : public IProcessCommunicator { +public: + ProcessCommunicatorTestStub() {} + ~ProcessCommunicatorTestStub() override {} + + DBStatus Start(const std::string &processLabel) override + { + MST_LOG("Is the processLabel empty %d", (processLabel == "")); + return OK; + } + + // The Stop should only be called after Start successfully + DBStatus Stop() override + { + return OK; + } + + DBStatus RegOnDeviceChange(const OnDeviceChange &callback) override + { + MST_LOG("Is the OnDeviceChange callback is nullptr: %d", (callback == nullptr)); + return OK; + } + DBStatus RegOnDataReceive(const OnDataReceive &callback) override + { + MST_LOG("Is the OnDataReceive callback is nullptr: %d", (callback == nullptr)); + 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; // 1 * 1024 * 1024 Byte. + } + + 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 + { + MST_LOG("Is the DeviceInfos: %d", (peerDevInfo.identifier == "")); + return true; + } + + void SetCommErr(bool commErr) + { + isCommErr = commErr; + } +private: + bool isCommErr = false; +}; +} // namespace DistributedDB + +#endif // PROCESSCOMMUNICATOR_TEST_STUB_H_H diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributed_crud_transaction_tools.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributed_crud_transaction_tools.cpp new file mode 100755 index 000000000..6fb0ff98f --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributed_crud_transaction_tools.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 "distributed_crud_transaction_tools.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "distributed_test_tools.h" +#include "distributeddb_data_generator.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace chrono; +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +DistributedCrudTransactionTools::DistributedCrudTransactionTools(KvStoreDelegate &delegate, + CrudMode first, CrudMode second, bool preset, bool isLocal) +{ + this->storeDelegate_ = &delegate, + this->firstMode_ = first; + this->secondMode_ = second; + this->needPresetData_ = preset; + this->isLocal_ = isLocal; +} + +bool DistributedCrudTransactionTools::PresetValue() +{ + vector entriesBatch; + vector allKeys; + GenerateRecords(this->presetCount_, DEFAULT_START, allKeys, entriesBatch); + + for (unsigned long i = 0; i < this->presetCount_; ++i) { + entriesBatch[i].value = GetValueWithInt(this->presetValue_); + } + DBStatus status = DistributedTestTools::PutBatch(*storeDelegate_, entriesBatch); + if (status != DistributedDB::OK) { + MST_LOG("PresetValue failed, status %d", status); + return false; + } + return true; +} + +bool DistributedCrudTransactionTools::CheckFirst() +{ + vector values = DistributedTestTools::GetEntries(*storeDelegate_, KEY_SEARCH_4); + if (firstMode_ == CrudMode::PUT_BATCH) { + MST_LOG("[CHECK LOG]putbatch first %zu", values.size()); + if (needPresetData_) { + if (values.size() != 0) { + return GetIntValue(values[0].value) == presetValue_; + } else { + return true; + } + } else { + return (values.size() == NO_RECORD) || (values.size() == presetCount_); + } + } + if (firstMode_ == CrudMode::UPDATE_BATCH) { + if (values.size() != presetCount_) { + return false; + } + for (unsigned long index = 0; index < values.size(); ++index) { + if (GetIntValue(values[index].value) != presetValue_) { + return false; + } + } + return true; + } + if (firstMode_ == CrudMode::DELETE_BATCH || firstMode_ == CrudMode::CLEAR) { + MST_LOG("[CHECK LOG]check clear first %zu", values.size()); + if ((values.size() > NO_RECORD) && (values.size() < presetCount_)) { + return false; + } + return (values.size() == NO_RECORD) || + ((values.size() == presetCount_ && GetIntValue(values[0].value) == presetValue_)); + } + return false; +} + +bool DistributedCrudTransactionTools::CheckSecond() +{ + if (secondMode_ == CrudMode::PUT) { + vector values = DistributedTestTools::GetEntries(*storeDelegate_, KEY_SEARCH_4); + return values.size() == NO_RECORD || GetIntValue(values[0].value) == SMALL_VALUE_SIZE || + GetIntValue(values[0].value) == presetValue_; + } + if (secondMode_ == CrudMode::DELETE) { + Key key0 = { 'k', '0' }; + Value value = DistributedTestTools::Get(*storeDelegate_, key0); + return value.size() == NO_RECORD || GetIntValue(value) == presetValue_; + } + if (secondMode_ == CrudMode::PUT_BATCH) { + vector values = DistributedTestTools::GetEntries(*storeDelegate_, KEY_SEARCH_4); + if ((values.size() != NO_RECORD) && (values.size() != presetCount_)) { + return false; + } + for (unsigned long index = 0; index < values.size(); ++index) { + if (GetIntValue(values[0].value) != presetValue_) { + return false; + } + } + return true; + } + if (secondMode_ == CrudMode::DELETE_BATCH || secondMode_ == CrudMode::CLEAR) { + vector values = DistributedTestTools::GetEntries(*storeDelegate_, KEY_SEARCH_4); + if ((values.size() != NO_RECORD) && (values.size() != presetCount_)) { + return false; + } + for (unsigned long index = 0; index < values.size(); ++index) { + if (GetIntValue(values[0].value) != presetValue_) { + return false; + } + } + return true; + } + return false; +} + +void DistributedCrudTransactionTools::Check() +{ + while (!secondComplete_) { + if (!firstComplete_) { + bool result = CheckFirst(); + if (!result) { + MST_LOG("[CHECK LOG]check first failed;%d", success_); + } + MST_LOG("[CHECK LOG]firstComplete_ failed %d;", success_); + success_ = result; + } else if (firstComplete_ && !secondComplete_) { + bool result = CheckSecond(); + if (!result) { + MST_LOG("[CHECK LOG]check second failed;%d", success_); + } + MST_LOG("[CHECK LOG]secondComplete_ failed %d;", success_); + success_ = result; + } + } +} + +bool DistributedCrudTransactionTools::Action1(KvStoreDelegate &delegate) +{ + MST_LOG("firstmode %d", static_cast(firstMode_)); + if (firstMode_ == CrudMode::PUT_BATCH) { + return DBStatus::OK == delegate.PutBatch(entriesBatch_); + } else if (firstMode_ == CrudMode::DELETE_BATCH) { + return DBStatus::OK == delegate.DeleteBatch(allKeys_) || + DBStatus::NOT_FOUND == delegate.DeleteBatch(allKeys_); + } else if (firstMode_ == CrudMode::CLEAR) { + return DBStatus::OK == delegate.Clear(); + } else { + MST_LOG("unknown first %d", static_cast(firstMode_)); + return false; + } +} + +bool DistributedCrudTransactionTools::Action2(KvStoreDelegate &delegate) +{ + MST_LOG("secondmode %d", static_cast(secondMode_)); + if (secondMode_ == CrudMode::PUT) { + entriesBatch_[0].value = GetValueWithInt(SMALL_VALUE_SIZE); + return DBStatus::OK == delegate.Put(entriesBatch_[0].key, entriesBatch_[0].value); + } else if (secondMode_ == CrudMode::DELETE) { + return DBStatus::OK == delegate.Delete(entriesBatch_[0].key) || + DBStatus::NOT_FOUND == delegate.DeleteBatch(allKeys_); + } else if (secondMode_ == CrudMode::CLEAR) { + return DBStatus::OK == delegate.Clear(); + } else if (secondMode_ == CrudMode::PUT_BATCH) { + return DBStatus::OK == delegate.PutBatch(entriesBatch_); + } else { + MST_LOG("unknown secondmode %d", static_cast(secondMode_)); + return false; + } +} + +void SleepOneSecond() +{ + std::this_thread::sleep_for(std::chrono::duration(1)); +} + +bool DeleteDataBase(bool success_, KvStoreDelegate *delegate1, KvStoreDelegate *delegate2, + KvStoreDelegateManager *delegateManager1, KvStoreDelegateManager *delegateManager2) +{ + SleepOneSecond(); + if ((delegateManager1->CloseKvStore(delegate1) != OK) || + (delegateManager2->CloseKvStore(delegate2) != OK)) { + MST_LOG("closed failed!"); + } + delegate1 = nullptr; + delegate2 = nullptr; + delete delegateManager1; + delegateManager1 = nullptr; + delete delegateManager2; + delegateManager2 = nullptr; + MST_LOG("[CHECK LOG]check result %d", success_); + return success_; +} + +bool DistributedCrudTransactionTools::testCrudTransaction() +{ + if (storeDelegate_ == nullptr) { + return false; + } + + DistributedTestTools::Clear(*storeDelegate_); + if (this->needPresetData_) { + if (!PresetValue()) { + return false; + } + } + GenerateRecords(this->presetCount_, DEFAULT_START, allKeys_, entriesBatch_); + for (unsigned long i = 0; i < this->presetCount_; ++i) { + entriesBatch_[i].value = GetValueWithInt(this->presetValue_); + } + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegateManager *delegateManager1 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(delegateManager1, + g_kvdbParameter1, g_kvOption); + if (delegateManager1 == nullptr || delegate1 == nullptr) { + MST_LOG("[testCrudTransaction] delegateManager1 or delegate1 is nullptr"); + return false; + } + + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *delegateManager2 = nullptr; + delegate2 = DistributedTestTools::GetDelegateSuccess(delegateManager2, + g_kvdbParameter1, g_kvOption); + if (delegateManager2 == nullptr || delegate2 == nullptr) { + MST_LOG("[testCrudTransaction] delegateManager2 or delegate2 is nullptr"); + return false; + } + + std::thread th(&DistributedCrudTransactionTools::Check, this); + th.detach(); + + if (!Action1(*delegate1)) { + MST_LOG("action1 failed"); + goto ERROR; + } + firstComplete_ = true; + MST_LOG("firstComplete_"); + + if (!Action2(*delegate2)) { + MST_LOG("action2 failed"); + goto ERROR; + } + secondComplete_ = true; + return DeleteDataBase(success_, delegate1, delegate2, delegateManager1, delegateManager2); +ERROR: + secondComplete_ = true; + return false; +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_backup_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_backup_test.cpp new file mode 100755 index 000000000..4df1bc31d --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_backup_test.cpp @@ -0,0 +1,1226 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" +#include "process_communicator_test_stub.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvBackup { +static std::condition_variable g_kvBackupVar; +class DistributeddbKvBackupTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +KvStoreDelegate *g_kvBackupDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_manager = nullptr; +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +DistributedDB::CipherPassword g_filePasswd1; +DistributedDB::CipherPassword g_filePasswd2; +void DistributeddbKvBackupTest::SetUpTestCase(void) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + manager->SetProcessLabel("MST", "GetDevicesID"); + manager->SetProcessCommunicator(std::make_shared()); + delete manager; + manager = nullptr; + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + (void)g_filePasswd1.SetValue(FILE_PASSWD_VECTOR_1.data(), FILE_PASSWD_VECTOR_1.size()); + (void)g_filePasswd2.SetValue(FILE_PASSWD_VECTOR_2.data(), FILE_PASSWD_VECTOR_2.size()); +} + +void DistributeddbKvBackupTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvBackupTest::SetUp(void) +{ + const std::string exportPath = DIRECTOR + "export"; + RemoveDatabaseDirectory(exportPath); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + KvOption option = g_kvOption; + g_kvBackupDelegate = DistributedTestTools::GetDelegateSuccess(g_manager, g_kvdbParameter1, option); + ASSERT_TRUE(g_manager != nullptr && g_kvBackupDelegate != nullptr); +} + +void DistributeddbKvBackupTest::TearDown(void) +{ + MST_LOG("TearDown after case."); + EXPECT_EQ(g_manager->CloseKvStore(g_kvBackupDelegate), OK); + g_kvBackupDelegate = nullptr; + DBStatus status = g_manager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, OK) << "fail to delete exist kvdb"; + delete g_manager; + g_manager = nullptr; +} + +/* + * @tc.name: ExportTest 001 + * @tc.desc: test checking in parameter of filepath of export interface . + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call export interface and the dir of filepath is nonexistent. + * @tc.expected: step1. call failed and return INVALID_ARGS. + */ + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + const std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), INVALID_ARGS); + + /** + * @tc.steps: step2. call export interface, the filepath is absolute path and legal then check file number. + * @tc.expected: step2. call successfully and the file number is 1. + */ + SetDir(exportPath); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath); + RemoveDatabaseDirectory(exportPath); + + /** + * @tc.steps: step3. call export interface to export data and the dir of filepath is no r,w right. + * @tc.expected: step3. call failed return INVALID_ARGS. + */ +#ifdef RUNNING_ON_SIMULATED_ENV + const std::string noRightPath = "../noright"; + const int authRight = 0111; + SetDir(noRightPath, authRight); + const std::string filePath2 = noRightPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath2, NULL_PASSWD), NO_PERMISSION); + RemoveDatabaseDirectory(noRightPath); +#endif + /** + * @tc.steps: step4. call export interface, the filepath is relative path and legal then check file number. + * @tc.expected: step4. call successfully and the file number is 1. + */ + const std::string exportPath2 = "../export"; + SetDir(exportPath2); + const std::string filePath3 = exportPath2 + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath3, NULL_PASSWD), OK); + CheckFileNumber(exportPath2, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath2); +} + +/* + * @tc.name: ExportTest 002 + * @tc.desc: Verify that the export interface will return FILE_ALREADY_EXISTED if the export file has been exist. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. call export interface and the dir of filepath is legal then check file number. + * @tc.expected: step1. call successfully and the file number of filepath is 1. + */ + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + const std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); // the number of files is 1. + + /** + * @tc.steps: step2. call export interface and the directory of file is existent. + * @tc.expected: step2. call failed and return FILE_ALREADY_EXISTED. + */ + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), FILE_ALREADY_EXISTED); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); // the number of files is 1. + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest003 + * @tc.desc: Verify that call export will be block when there is uncommit or rollback transaction. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest003, TestSize.Level3) +{ + /** + * @tc.steps: step1. start transaction and call export interface to export data. + * @tc.expected: step1. start transaction successfully call export is blocked. + */ + EXPECT_EQ(g_kvBackupDelegate->StartTransaction(), OK); + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB1.bin"; + DBStatus status = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + + /** + * @tc.steps: step2. start thread to commit transaction and check the number od files in the exportion directory. + * @tc.expected: step2. commit successfully and the number of files is 1. + */ + bool exportFlag = false; + thread subThread1([&]() { + DBStatus status = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(status, OK); + exportFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread1.detach(); + EXPECT_EQ(g_kvBackupDelegate->Commit(), OK); + std::mutex count; + { + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return exportFlag;}); + } + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); // the number of files is 1. + + /** + * @tc.steps: step3. start transaction and call export interface to export data. + * @tc.expected: step3. start transaction successfully call export is blocked. + */ + exportFlag = false; + EXPECT_EQ(g_kvBackupDelegate->StartTransaction(), OK); + filePath = exportPath + "/bkpDB2.bin"; + + /** + * @tc.steps: step3. start thread to rollback transaction and check the number od files in the exportion directory. + * @tc.expected: step3. rollback successfully and the number of files is 2. + */ + thread subThread2([&]() { + status = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(status, OK); + exportFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread2.detach(); + EXPECT_EQ(g_kvBackupDelegate->Rollback(), OK); + { + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return exportFlag;}); + } + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the number of files is 2. + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 004 + * @tc.desc: Verify that the export interface will return busy when the import hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest004, TestSize.Level2) +{ + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + DBStatus status = DistributedTestTools::PutBatch(*g_kvBackupDelegate, entriesBatch); + ASSERT_TRUE(status == DBStatus::OK); + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully. + */ + bool importFlag = false; + thread subThread([&]() { + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), OK); + importFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. return busy if step1 hasn't completed, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_TWO_HUNDREDS_MS)); + filePath = exportPath + "/bkpDB1.bin"; + status = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_TRUE(status == BUSY || status == OK); + CheckFileNumber(exportPath, fileCount); + if (status == BUSY) { + EXPECT_EQ(fileCount, 1); // the number of file is 1(Export is busy). + } else { + EXPECT_EQ(fileCount, 2); // the number of file is 2(Export is OK). + } + + std::mutex count; + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return importFlag;}); + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 005 + * @tc.desc: Verify that the export interface will return busy when the rekey hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest005, TestSize.Level2) +{ + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + DBStatus status = DistributedTestTools::PutBatch(*g_kvBackupDelegate, entriesBatch); + ASSERT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully. + */ + bool rekeyFlag = false; + thread subThread([&]() { + DBStatus rekeyStatus = g_kvBackupDelegate->Rekey(g_passwd1); + EXPECT_EQ(rekeyStatus, OK); + rekeyFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. return busy if step1 hasn't completed, else return OK. + */ + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + const std::string filePath = exportPath + "/bkpDB.bin"; + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_TWO_HUNDREDS_MS)); + status = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_TRUE(status == BUSY || status == OK); + CheckFileNumber(exportPath, fileCount); + if (status == BUSY) { + EXPECT_EQ(fileCount, 0); // 0 file if export failed. + } else { + EXPECT_EQ(fileCount, 1); // 1 file if export success. + } + + std::mutex count; + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return rekeyFlag;}); + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 006 + * @tc.desc: Verify that the export interface will execute blockly when last export hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest006, TestSize.Level2) +{ + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + DBStatus status = DistributedTestTools::PutBatch(*g_kvBackupDelegate, entriesBatch); + ASSERT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully. + */ + bool exportFlag = false; + thread subThread([&]() { + DBStatus exportStatus = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(exportStatus, OK); + exportFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. return OK. + */ + std::string filePath2 = exportPath + "/bkpDB1.bin"; + std::this_thread::sleep_for(std::chrono::microseconds(MILLSECONDES_PER_SECOND)); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + + std::mutex count; + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return exportFlag;}); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the file number is 2. + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 007 + * @tc.desc: Verify that the put/delete operation will execute blockly when export hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest007, TestSize.Level2) +{ + vector entriesPut; + entriesPut.push_back(ENTRY_1); + entriesPut.push_back(ENTRY_2); + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + ASSERT_TRUE(DistributedTestTools::PutBatch(*g_kvBackupDelegate, entriesBatch) == OK); + + /** + * @tc.steps: step1. call export interface in subthread. + * @tc.expected: step1. call successfully. + */ + bool exportFlag = false; + thread subThread([&]() { + DBStatus exportStatus = g_kvBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(exportStatus, OK); + exportFlag = true; + g_kvBackupVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call Get,GetLocal,GetEntries to query data from db. + * @tc.expected: step2. call successfully. + */ + Value valueResult = DistributedTestTools::Get(*g_kvBackupDelegate, allKeys[0]); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, entriesBatch[0].value)); + vector entries = DistributedTestTools::GetEntries(*g_kvBackupDelegate, KEY_EMPTY); + ASSERT_EQ(entries.size(), entriesBatch.size()); + + /** + * @tc.steps: step3. call put/delete/putlocal/deletelocal interface when step1 is running. + * @tc.expected: step3. return ok. + */ + DBStatus status = DistributedTestTools::Put(*g_kvBackupDelegate, entriesBatch[0].key, entriesBatch[0].value); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedTestTools::PutBatch(*g_kvBackupDelegate, entriesPut), OK); + EXPECT_EQ(DistributedTestTools::Delete(*g_kvBackupDelegate, KEY_1), OK); + EXPECT_EQ(DistributedTestTools::DeleteBatch(*g_kvBackupDelegate, allKeys), OK); + + std::mutex count; + std::unique_lock lck(count); + g_kvBackupVar.wait(lck, [&]{return exportFlag;}); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 008 + * @tc.desc: Verify that passwd parameter of export interface decide the file exported is encrypted or not. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ExportTest008, TestSize.Level1) +{ + int fileCount = 0; + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB1.bin"; + + /** + * @tc.steps: step1. call export interface in subthread. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step2. call export interface with the passwd = password(129B). + * @tc.expected: step2. return INVALID_ARGS. + */ + filePath = exportPath + "/bkpDB2.bin"; + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step3. call export interface with the passwd = password(1B). + * @tc.expected: step3. return OK. + */ + filePath = exportPath + "/bkpDB3.bin"; + passwordVector.assign(1, 'b'); // 1 Byte of passwd. + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, password), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the number of file is 2. + + /** + * @tc.steps: step3. call export interface with the passwd = password(128B). + * @tc.expected: step3. return OK. + */ + filePath = exportPath + "/bkpDB4.bin"; + passwordVector.assign(BATCH_RECORDS, 'b'); // 1 Byte of passwd. + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, password), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 3); // the number of file is 3. + RemoveDir(exportPath); +} + +/* + * @tc.name: ImportTest 001 + * @tc.desc: Verify that call import bkpfile with right password: none <-> none, password1 <-> password1. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest001, TestSize.Level2) +{ + /** + * @tc.steps: step1. put (k1, v1) to device and update it to (k1, v2) after export the backup file. + * @tc.expected: step1. put and update successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_2), OK); + + /** + * @tc.steps: step2. call import interface with password f1. + * @tc.expected: step2. call failed and return INVALID_FILE. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, g_filePasswd1), INVALID_FILE); + + /** + * @tc.steps: step3. call import interface with empty password and get the value of k1. + * @tc.expected: step3. call import interface successfully and the value of k1 is v1 + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), OK); + Value valueGot = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueGot, VALUE_1)); + + /** + * @tc.steps: step4. put (k1, v2) to device for there is already encrypted backup file to update it to (k1, v1). + * @tc.expected: step4. put and update successfully. + */ + filePath = importPath + "/bkpDB2.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step5. call import interface with empty password. + * @tc.expected: step5. call failed and return INVALID_FILE. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_FILE); + + /** + * @tc.steps: step6. call import interface with db password p1. + * @tc.expected: step6. call failed and return INVALID_FILE + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, g_passwd1), INVALID_FILE); + + /** + * @tc.steps: step7. call import interface with passwd = password(129B). + * @tc.expected: step7. call failed and return INVALID_ARGS + */ + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword invalidPassword; + EXPECT_EQ(invalidPassword.SetValue(passwordVector.data(), passwordVector.size()), + CipherPassword::ErrorCode::OVERSIZE); + + /** + * @tc.steps: step8. call import interface with password f1 and get the value of k1. + * @tc.expected: step8. call import interface successfully and the value of k1 is v1 + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, g_filePasswd1), OK); + valueGot.clear(); + valueGot = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueGot, VALUE_1)); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 002 + * @tc.desc: Verify that can't import when the file is not exist or the file path is wrong. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest002, TestSize.Level1) +{ + std::string importPath1 = DIRECTOR + "export"; + SetDir(importPath1); + std::string filePath = importPath1 + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. import the backup file 1 in the noexsit path. + * @tc.expected: step1. call failed and return INVALID_FILE. + */ + std::string importPath2 = DIRECTOR + "noexsit"; + filePath = importPath2 + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_ARGS); + + /** + * @tc.steps: step2. import the no exist backup file 2 in the noexsit path. + * @tc.expected: step2. call failed and return INVALID_FILE + */ + filePath = importPath1 + "/bkpDB2.bin"; + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_FILE); + RemoveDir(importPath1); +} + +/* + * @tc.name: ImportTest 003 + * @tc.desc: Verify that can't import when the file is not a right DB file or the DB file is damaged. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest003, TestSize.Level1) +{ + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. import a.txt in the right path. + * @tc.expected: step1. call failed and return INVALID_FILE. + */ + filePath = importPath + "/a.txt"; + ofstream createFile(filePath); + if (createFile) { + createFile << '1' << endl; + createFile.close(); + } + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_FILE); + + /** + * @tc.steps: step2. write some data to DB file1. + * @tc.expected: step2. write successfully + */ + filePath = importPath + "/bkpDB.bin"; + ofstream damageFile(filePath, ios::out | ios::binary); + ASSERT_TRUE(damageFile.is_open()); + damageFile.write(reinterpret_cast(&filePath), filePath.size()); + damageFile.close(); + + /** + * @tc.steps: step3. import DB file with empty password. + * @tc.expected: step3. import failed and returned INVALID_FILE + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_FILE); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 005 + * @tc.desc: Verify that import will return busy when there are many delegate of the same db. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest005, TestSize.Level1) +{ + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, g_filePasswd1), OK); + + /** + * @tc.steps: step1. open the other delegate of the same kvstore. + * @tc.expected: step1. open successfully. + */ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvOption option = g_kvOption; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter1, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + /** + * @tc.steps: step2. import the backup file of the same kvstore of g_kvdbParameter1. + * @tc.expected: step2. import failed and returned BUSY. + */ + EXPECT_EQ(delegate2->Import(filePath, g_filePasswd1), BUSY); + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + delete manager2; + manager2 = nullptr; + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 006 + * @tc.desc: Verify that if the DB register observer or snapshot, it can't import DB backup file. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest006, TestSize.Level1) +{ + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, g_filePasswd1), OK); + + /** + * @tc.steps: step1. register the observer of DB of the same kvstore. + * @tc.expected: step1. create successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_kvBackupDelegate, &observer); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. import the backup file of the DB. + * @tc.expected: step2. import failed and returned BUSY. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, g_filePasswd1), BUSY); + /** + * @tc.steps: step3. unregister the observer and register the snap shot. + * @tc.expected: step3. unregister observer and register the snap shot successfully. + */ + status = DistributedTestTools::UnRegisterObserver(g_kvBackupDelegate, &observer); + EXPECT_EQ(status, OK); + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_kvBackupDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + /** + * @tc.steps: step4. import the backup file of the DB second time. + * @tc.expected: step4. import failed and returned BUSY. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, g_filePasswd1), BUSY); + EXPECT_EQ(g_kvBackupDelegate->ReleaseKvStoreSnapshot(snapShot), OK); + + snapShot = nullptr; + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 007 + * @tc.desc: Verify that it will return busy if execute CRUD during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest007, TestSize.Level2) +{ + vector entries; + std::vector allKeys; + GenerateFixedRecords(entries, allKeys, TEN_RECORDS, KEY_SIX_BYTE, ONE_M_LONG_STRING); + for (auto iter = entries.begin(); iter != entries.end(); iter++) { + EXPECT_EQ(g_kvBackupDelegate->Put(iter->key, iter->value), OK); + } + + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB1.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start the sub thread to import a very big backup DB file. + * @tc.expected: step1. import successfully if step1 running before step2. + */ + bool importFlag = false; + thread subThread([&]() { + DBStatus importStatus = g_kvBackupDelegate->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(importStatus == OK || importStatus == BUSY); + importFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. get kvstore again during the sub thread is importing backup file. + * @tc.expected: step2. get failed and return BUSY if step1 is running, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig(KV_CONFIG), OK); + DelegateKvMgrCallback getDelegateCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &getDelegateCallback, std::placeholders::_1, std::placeholders::_2); + KvStoreDelegate::Option option = DistributedTestTools::TransferKvOptionType(g_kvOption); + manager->GetKvStore(g_kvdbParameter1.storeId, option, function); + DBStatus status = getDelegateCallback.GetStatus(); + EXPECT_TRUE(status == BUSY || status == OK); + /** + * @tc.steps: step3. CRUD on DB during the sub thread is importing backup file. + * @tc.expected: step3. put failed and return BUSY. + */ + status = g_kvBackupDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == BUSY || status == OK); + status = g_kvBackupDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == BUSY || status == OK); + status = g_kvBackupDelegate->Delete(KEY_1); + EXPECT_TRUE(status == BUSY || status == OK); + + std::mutex reGetKvStoreMtx; + std::unique_lock lck(reGetKvStoreMtx); + g_kvBackupVar.wait(lck, [&]{return importFlag;}); + delete manager; + manager = nullptr; + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 008 + * @tc.desc: Verify that it will return busy if it rekey during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest008, TestSize.Level2) +{ + vector entriesRekey; + std::vector allKeys; + GenerateFixedRecords(entriesRekey, allKeys, TEN_RECORDS, KEY_SIX_BYTE, ONE_M_LONG_STRING); + for (auto iter = entriesRekey.begin(); iter != entriesRekey.end(); iter++) { + EXPECT_EQ(g_kvBackupDelegate->Put(iter->key, iter->value), OK); + } + + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start the sub thread to import a very big backup DB file. + * @tc.expected: step1. import successfully. + */ + bool importFlag = false; + thread subThread([&]() { + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), OK); + importFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. rekey kvstore use p1 during the sub thread is importing backup file. + * @tc.expected: step2. rekey failed and return BUSY if step1 hasn't completed, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_TWO_HUNDREDS_MS)); + DBStatus status = g_kvBackupDelegate->Rekey(g_passwd1); + EXPECT_TRUE(status == BUSY || status == OK); + + std::mutex reGetKvStoreMtx; + std::unique_lock lck(reGetKvStoreMtx); + g_kvBackupVar.wait(lck, [&]{return importFlag;}); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 009 + * @tc.desc: Verify that it will return busy if it import during it is rekeying. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest009, TestSize.Level2) +{ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvOption option = g_createKvDiskUnencrypted; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + + vector entriesRekey; + std::vector allKeys; + GenerateFixedRecords(entriesRekey, allKeys, TEN_RECORDS, KEY_SIX_BYTE, ONE_M_LONG_STRING); + for (auto iter = entriesRekey.begin(); iter != entriesRekey.end(); iter++) { + EXPECT_EQ(delegate2->Put(iter->key, iter->value), OK); + } + + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + delegate2->Export(filePath, NULL_PASSWD); + + /** + * @tc.steps: step1. start the sub thread to import a very big backup DB file. + * @tc.expected: step1. import successfully. + */ + bool rekeyFlag = false; + thread subThread([&]() { + EXPECT_EQ(delegate2->Rekey(g_passwd1), OK); + rekeyFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. rekey kvstore use p1 during the sub thread is importing backup file. + * @tc.expected: step2. rekey failed and return BUSY if step1 hasn't completed, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_TWO_HUNDREDS_MS)); + DBStatus status = delegate2->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(status == BUSY || status == OK); + + std::mutex rekeyMtx; + std::unique_lock lck(rekeyMtx); + g_kvBackupVar.wait(lck, [&]{return rekeyFlag;}); + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 010 + * @tc.desc: Verify that it will return busy if it import again during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest010, TestSize.Level2) +{ + vector entriesRekey2; + std::vector allKeys; + GenerateFixedRecords(entriesRekey2, allKeys, TEN_RECORDS, KEY_SIX_BYTE, ONE_M_LONG_STRING); + for (auto iter = entriesRekey2.begin(); iter != entriesRekey2.end(); iter++) { + EXPECT_EQ(g_kvBackupDelegate->Put(iter->key, iter->value), OK); + } + + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_kvBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start the sub thread to import a very big backup DB file. + * @tc.expected: step1. import successfully. + */ + bool importFlag = false; + thread subThread([&]() { + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), OK); + importFlag = true; + g_kvBackupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. import the backup file again during the sub thread is importing. + * @tc.expected: step2. call import interface failed and return BUSY. + */ + std::this_thread::sleep_for(std::chrono::microseconds(MILLSECONDES_PER_SECOND)); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath, NULL_PASSWD), OK); + + std::mutex reImportMtx; + std::unique_lock lck(reImportMtx); + g_kvBackupVar.wait(lck, [&]{return importFlag;}); + RemoveDir(importPath); +} + +void KvSubImportThread(int index, std::string importPath) +{ + vector tenEntries; + std::vector allKeys; + GenerateFixedRecords(tenEntries, allKeys, TEN_RECORDS, KEY_SIX_BYTE, FOUR_M_LONG_STRING); + /** + * @tc.steps: step1. every thread create one DB put 10 entries into the DB. + * @tc.expected: step1. put successfully. + */ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvOption option[] = {g_createKvDiskUnencrypted, g_createKvDiskUnencrypted, g_createKvDiskUnencrypted, + g_createKvDiskEncrypted, g_createKvDiskEncrypted}; + KvDBParameters parameter[] = {g_kvdbParameter2, g_kvdbParameter3, g_kvdbParameter4, + g_kvdbParameter5, g_kvdbParameter6}; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, parameter[index], option[index]); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + for (auto iter = tenEntries.begin(); iter != tenEntries.end(); iter++) { + EXPECT_EQ(delegate2->Put(iter->key, iter->value), OK); + } + + /** + * @tc.steps: step2. every thread export DB to backup file. + * @tc.expected: step2. export successfully. + */ + DistributedDB::CipherPassword passwd[] = {NULL_PASSWD, NULL_PASSWD, NULL_PASSWD, g_filePasswd1, g_filePasswd2}; + const std::string backupFile[] = {"/bkpDB1.bin", "/bkpDB2.bin", "/bkpDB3.bin", "/bkpDB4.bin", "/bkpDB5.bin"}; + std::string filePath[] = {(importPath + backupFile[INDEX_ZEROTH]), (importPath + backupFile[INDEX_FIRST]), + (importPath + backupFile[INDEX_SECOND]), (importPath + backupFile[INDEX_THIRD]), + (importPath + backupFile[INDEX_FORTH])}; + delegate2->Export(filePath[index], passwd[index]); + + /** + * @tc.steps: step3. every thread import backup file to DB. + * @tc.expected: step3. import successfully. + */ + EXPECT_EQ(delegate2->Import(filePath[index], passwd[index]), OK); + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + std::string storeId[] = {STORE_ID_2, STORE_ID_3, STORE_ID_4, STORE_ID_5, STORE_ID_6}; + EXPECT_EQ(manager2->DeleteKvStore(storeId[index]), OK); + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: ImportTest 011 + * @tc.desc: Verify that different DB export and import in the same time don't affect each other. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, ImportTest011, TestSize.Level3) +{ + std::vector threads; + const std::string importPath = DIRECTOR + "export"; + SetDir(importPath); + for (int index = 0; index < FIVE_TIMES; ++index) { + threads.push_back(std::thread(KvSubImportThread, index, std::ref(importPath))); + } + + for (auto& th : threads) { + th.join(); + } + RemoveDir(importPath); +} + +/* + * @tc.name: Exchange 001 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, Exchange001, TestSize.Level2) +{ + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step3. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step4. rekey db1 with g_passwd1, and then import "bkpDB1.bin" and "bkpDB2.bin" with p1. + * @tc.expected: step4. rekey succeeded and import failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step5. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step5. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step6. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + RemoveDir(exportPath); +} + +/* + * @tc.name: Exchange 002 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, Exchange002, TestSize.Level2) +{ + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. rekey db1 with g_passwd1, + * export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd1. + * @tc.expected: step3. rekey succeeded and import failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step4. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step4. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step5. rekey db1 with NULL_PASSWD, + * import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step5. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_kvBackupDelegate->Rekey(NULL_PASSWD), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step6. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + RemoveDir(exportPath); +} + +/* + * @tc.name: Exchange 003 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvBackupTest, Exchange003, TestSize.Level2) +{ + const std::string exportPath = DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. rekey db1 with g_passwd1, + * export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. rekey db1 with g_passwd2, + * import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_kvBackupDelegate->Rekey(g_passwd2), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. Put (k1,v1), close db, open db with g_passwd2 and delete (k1,v1) + * @tc.expected: step3. operate successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ((g_manager->CloseKvStore(g_kvBackupDelegate)), OK); + g_kvBackupDelegate = nullptr; + delete g_manager; + g_manager = nullptr; + KvOption kvEncrypted(true, false, true, DistributedDB::CipherType::DEFAULT, + DistributedDBDataGenerator::PASSWD_VECTOR_2); + g_kvBackupDelegate = DistributedTestTools::GetDelegateSuccess(g_manager, g_kvdbParameter1, kvEncrypted); + ASSERT_TRUE(g_manager != nullptr && g_kvBackupDelegate != nullptr); + EXPECT_EQ(g_kvBackupDelegate->Delete(KEY_1), OK); + /** + * @tc.steps: step4. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd1. + * @tc.expected: step4. import failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step5. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd2. + * @tc.expected: step5. import failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_passwd2), INVALID_FILE); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_passwd2), INVALID_FILE); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step6. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_kvBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_kvBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step7. Put (k2,v2), close db, open db with g_passwd2 and delete (k2,v2) + * @tc.expected: step7. operate successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_2, VALUE_2), OK); + EXPECT_EQ((g_manager->CloseKvStore(g_kvBackupDelegate)), OK); + g_kvBackupDelegate = nullptr; + delete g_manager; + g_manager = nullptr; + g_kvBackupDelegate = DistributedTestTools::GetDelegateSuccess(g_manager, g_kvdbParameter1, kvEncrypted); + ASSERT_TRUE(g_manager != nullptr && g_kvBackupDelegate != nullptr); + EXPECT_EQ(g_kvBackupDelegate->Delete(KEY_2), OK); + RemoveDir(exportPath); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_batch_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_batch_crud_test.cpp new file mode 100755 index 000000000..76d9305ea --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_batch_crud_test.cpp @@ -0,0 +1,1084 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace std::placeholders; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvBatchCrud { +class DistributeddbKvBatchCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +const bool IS_LOCAL = false; +KvStoreDelegate *g_kvStoreBatchDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_batchManager = nullptr; +void DistributeddbKvBatchCrudTest::SetUpTestCase(void) +{ + MST_LOG("SetUpTestCase before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + KvOption option = g_kvOption; + option.localOnly = IS_LOCAL; + g_kvStoreBatchDelegate = DistributedTestTools::GetDelegateSuccess(g_batchManager, + g_kvdbParameter1, option); + ASSERT_TRUE(g_batchManager != nullptr && g_kvStoreBatchDelegate != nullptr); +} + +void DistributeddbKvBatchCrudTest::TearDownTestCase(void) +{ + MST_LOG("TearDownTestCase after all cases."); + EXPECT_EQ(g_batchManager->CloseKvStore(g_kvStoreBatchDelegate), OK); + g_kvStoreBatchDelegate = nullptr; + DBStatus status = g_batchManager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, OK) << "fail to delete exist kvdb"; + delete g_batchManager; + g_batchManager = nullptr; + RemoveDir(DIRECTOR); +} + +void DistributeddbKvBatchCrudTest::SetUp(void) +{ + ASSERT_TRUE(g_kvStoreBatchDelegate != nullptr); + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbKvBatchCrudTest::TearDown(void) +{ +} + +class KvStoreObserverSnapImpl final : public KvStoreObserver { +public: + void OnChange(const KvStoreChangedData &data) + { + changed_++; + onChangeTime_ = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + MST_LOG("comes a change,changed[%ld]!!!", changed_); + MST_LOG("GetEntriesInserted().size() = %zu, GetEntriesUpdated() = %zu, GetEntriesDeleted() = %zu", + data.GetEntriesInserted().size(), data.GetEntriesUpdated().size(), data.GetEntriesDeleted().size()); + insertCrudList_.assign(data.GetEntriesInserted().begin(), data.GetEntriesInserted().end()); + updateCrudList_.assign(data.GetEntriesUpdated().begin(), data.GetEntriesUpdated().end()); + deleteCrudList_.assign(data.GetEntriesDeleted().begin(), data.GetEntriesDeleted().end()); + } + + KvStoreObserverSnapImpl() + { + insertCrudList_.clear(); + updateCrudList_.clear(); + deleteCrudList_.clear(); + changed_ = 0; + onChangeTime_ = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + } + + ~KvStoreObserverSnapImpl() + { + changed_ = 0; + } + + long GetChanged() + { + return changed_; + } + + const list GetInsertList() + { + return insertCrudList_; + } + + const list GetDeleteList() + { + return deleteCrudList_; + } + + const list GetUpdateList() + { + return updateCrudList_; + } + + microClock_type GetOnChangeTime() + { + return onChangeTime_; + } + + void Clear() + { + insertCrudList_.clear(); + deleteCrudList_.clear(); + updateCrudList_.clear(); + changed_ = 0; + } + +private: + list insertCrudList_ = {}; + list deleteCrudList_ = {}; + list updateCrudList_ = {}; + long changed_ = 0; + microClock_type onChangeTime_ + = std::chrono::time_point_cast(std::chrono::steady_clock::now()); +}; + +/* + * @tc.name: SimpleData 001 + * @tc.desc: Verify that can PutBatch(keys,values) and get. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData001, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries2; + entries2.push_back(ENTRY_3); + + /** + * @tc.steps: step1. create kv db and PutBatch (k1,v1)(k2,v2) then get. + * @tc.expected: step1. PutBatch successfully and get the value of k1 is v1,get the value of k2 is v2. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + EXPECT_EQ(status, OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + /** + * @tc.steps: step2. create kv db and PutBatch (k3,v3) then get. + * @tc.expected: step2. PutBatch successfully and get the value of k3 is v3. + */ + DBStatus status2 = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries2); + EXPECT_EQ(status2, OK); + Value valueResult2 = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_3); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult2, VALUE_3)); + + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); +} + +/* + * @tc.name: SimpleData 002 + * @tc.desc: Verify that can PutBatch(keys,values) and update them. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData002, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries1Up; + entries1Up.push_back(ENTRY_1_2); + entries1Up.push_back(ENTRY_2_3); + + /** + * @tc.steps: step1. create kv db and PutBatch (k1,v1)(k2,v2) then get. + * @tc.expected: step1. PutBatch successfully and get the value of k1 is v1,get the value of k2 is v2. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + EXPECT_EQ(status, OK); + + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + /** + * @tc.steps: step2. update (k1,v1)(k2,v2) to (k1,v2)(k2,v3). + * @tc.expected: step2. update successfully and get the value of k1 is v2,get the value of k2 is v3. + */ + status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1Up); + EXPECT_EQ(status, DBStatus::OK); + + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_3)); + + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); +} + +/* + * @tc.name: SimpleData 003 + * @tc.desc: Verify that can deletebatch list. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData003, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + /** + * @tc.steps: step1. create kv db and PutBatch (k1,v1)(k2,v2) then get. + * @tc.expected: step1. PutBatch successfully and get the value of k1 is v1,get the value of k2 is v2. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_EQ(status, DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + /** + * @tc.steps: step2. deleteBatch (k1)(k2) then get. + * @tc.expected: step2. deleteBatch successfully and get the value of k1 is null,get the value of k2 is null. + */ + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + + DBStatus status2 = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1); + + ASSERT_EQ(status2, DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_EQ(valueResult.size(), size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_EQ(valueResult.size(), size_t(0)); +} + +/* + * @tc.name: SimpleData 004 + * @tc.desc: Verify that can put batch with some null value. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData004, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_1_NULL); // KEY_1 with null value. + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. create kv db and PutBatch (k1,v1)(k2,v2) that v1=null. + * @tc.expected: step1. PutBatch successfully. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. get k1,k2 from db. + * @tc.expected: step2. get the value of k1 is null,get the value of k2 is not null. + */ + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_EQ(valueResult.size(), size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); +} + +/* + * @tc.name: SimpleData 005 + * @tc.desc: Verify that can not put batch with some null key. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData005, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_NULL_1); // null key with VALUE_1. + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. create kv db and PutBatch (k1,v1)(k2,v2) that k1=null. + * @tc.expected: step1. PutBatch failed. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_TRUE(status != DBStatus::OK); + + /** + * @tc.steps: step2. get k1,k2 from db. + * @tc.expected: step2. get the value of k1 is null,get the value of k2 is null. + */ + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_EQ(valueResult.size(), size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_EQ(valueResult.size(), size_t(0)); +} + +/* + * @tc.name: SimpleData 006 + * @tc.desc: Verify that get batch none exist data will get null. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData006, TestSize.Level0) +{ + /** + * @tc.steps: step1. clear kv db. + * @tc.expected: step1. Construct that none exist data. + */ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + /** + * @tc.steps: step2. get batch(k1)(k2). + * @tc.expected: step2. get null. + */ + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreBatchDelegate, KEY_SEARCH); + ASSERT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: SimpleData 007 + * @tc.desc: Verify that delete batch none exist data will return OK. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData007, TestSize.Level0) +{ + /** + * @tc.steps: step1. clear kv db. + * @tc.expected: step1. Construct that none exist data. + */ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + + /** + * @tc.steps: step2. delete batch none exist data. + * @tc.expected: step2. delete failed but return OK. + */ + DBStatus status = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1); + ASSERT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: SimpleData 008 + * @tc.desc: Verify that can get entries with prefix-key. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData008, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + vector entries1; + entries1.push_back(ENTRY_A_1); + entries1.push_back(ENTRY_A_2); + entries1.push_back(ENTRY_A_3); + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step1. get entries that prefix-key="ab". + * @tc.expected: step1. get 3 records which are key="abc","abcdasd","abcds". + */ + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreBatchDelegate, KEY_SEARCH); + MST_LOG("value size %zu", valueResult.size()); + EXPECT_EQ(valueResult.size(), THREE_RECORDS); + /** + * @tc.steps: step2. get entries that prefix-key="abcde". + * @tc.expected: step2. get 0 record. + */ + valueResult = DistributedTestTools::GetEntries(*g_kvStoreBatchDelegate, KEY_SEARCH_2); + EXPECT_EQ(valueResult.size(), size_t(0)); +} + +/* + * @tc.name: SimpleData 009 + * @tc.desc: Verify that get entries with null key will return all records. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData009, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + vector entries1; + entries1.push_back(ENTRY_A_1); + entries1.push_back(ENTRY_A_2); + entries1.push_back(ENTRY_A_3); + /** + * @tc.steps: step1. PutBatch (k1=abc,v1=a1)(k2=abcdasd,v2=a2)(k3=abcds,v3=a3). + * @tc.expected: step1. PutBatch successfully to construct exist data. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. GetEntries with key=null. + * @tc.expected: step2. GetEntries successfully and return all records in db. + */ + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreBatchDelegate, KEY_EMPTY); + ASSERT_TRUE(valueResult.size() == entries1.size()); +} + +/* + * @tc.name: SimpleData 010 + * @tc.desc: Verify that can release snapshot. + * @tc.type: FUN + * @tc.require: SR000CQDTF + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, SimpleData010, TestSize.Level0) +{ + DelegateCallback delegateCallback; + function function + = bind(&DelegateCallback::Callback, &delegateCallback, _1, _2); + + g_kvStoreBatchDelegate->GetKvStoreSnapshot(nullptr, function); + DBStatus status = delegateCallback.GetStatus(); + EXPECT_EQ(status, DBStatus::OK); + KvStoreSnapshotDelegate *snapshot + = const_cast(delegateCallback.GetKvStoreSnapshot()); + EXPECT_NE(snapshot, nullptr); + /** + * @tc.steps: step1. release snapshot. + * @tc.expected: step1. release snapshot successfully. + */ + EXPECT_EQ(g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snapshot), OK); +} + +bool PutBatchTwoRecords(vector &entries) +{ + bool result = true; + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries); + result = result && (status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, entries[0].key); + result = result && (valueResult.size() != 0); + if (!result) { + MST_LOG("value.size() of entries[0].key is 0, and failed"); + } + result = result && (DistributedTestTools::IsValueEquals(valueResult, entries[0].value)); + if (!result) { + MST_LOG("value.size() Got of entries[0].key is not equal to the value entries[0].key Expected"); + } + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, entries[1].key); + result = result && (valueResult.size() != 0); + if (!result) { + MST_LOG("value.size() of entries[1].key is 0, and failed"); + } + result = result && (DistributedTestTools::IsValueEquals(valueResult, entries[1].value)); + if (!result) { + MST_LOG("value.size() Got of entries[1].key is not equal to the value entries[1].key Expected"); + } + return result; +} + +/* + * @tc.name: ComplexData 001 + * @tc.desc: Verify that can batch crud for same keys repeatedly. + * @tc.type: FUN + * @tc.require: SR000CQDTF + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, ComplexData001, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries12; + entries12.push_back(ENTRY_1_2); + entries12.push_back(ENTRY_2_3); + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + EXPECT_TRUE(PutBatchTwoRecords(entries1)); + + /** + * @tc.steps: step2. DeleteBatch (k1)(k2) and then get. + * @tc.expected: step2. DeleteBatch successfully get null of k1,k2. + */ + DBStatus status2 = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, KEYS_1); + ASSERT_EQ(status2, DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_EQ(valueResult.size(), size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_EQ(valueResult.size(), size_t(0)); + + /** + * @tc.steps: step3. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step3. PutBatch successfully get v1,v2 of k1,k2. + */ + EXPECT_TRUE(PutBatchTwoRecords(entries1)); + + /** + * @tc.steps: step4. PutBatch (k1,v2)(k2,v3) and then get. + * @tc.expected: step4. PutBatch successfully get v2 of k1,v3 of k2. + */ + EXPECT_TRUE(PutBatchTwoRecords(entries12)); + + /** + * @tc.steps: step5. DeleteBatch (k1)(k2) and then get. + * @tc.expected: step5. DeleteBatch successfully get null of k1,k2. + */ + status2 = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, KEYS_1); + EXPECT_EQ(status2, DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_EQ(valueResult.size(), size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_EQ(valueResult.size(), size_t(0)); +} + +void CheckBatchCrud(vector keys23) +{ + /** + * @tc.steps: step3. Put(k1,v3) Put(k2,v4) Put(k3,v3) and then get. + * @tc.expected: step3. Put successfully get v3 of k1,v4 of k2,v3 of k3. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, ENTRY_1_3.key, ENTRY_1_3.value); + ASSERT_EQ(status, DBStatus::OK); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, ENTRY_2_4.key, ENTRY_2_4.value); + ASSERT_EQ(status, DBStatus::OK); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, ENTRY_3.key, ENTRY_3.value); + ASSERT_EQ(status, DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_3)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_4)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_3); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_3)); + /** + * @tc.steps: step4. DeleteBatch (k2)(k3) and then get. + * @tc.expected: step4. DeleteBatch successfully get not null of k1,null of k2,k3. + */ + DBStatus status2 = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys23); + ASSERT_TRUE(status2 == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_NE(valueResult.size(), size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_3)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() == size_t(0)); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_3); + EXPECT_TRUE(valueResult.size() == size_t(0)); + /** + * @tc.steps: step5. clear all and then Put(k1,v1) then get. + * @tc.expected: step5. clear and Put successfully, get v1 of k1. + */ + status2 = DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + ASSERT_TRUE(status2 == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == size_t(0)); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, ENTRY_1.key, ENTRY_1.value); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != size_t(0)); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); +} + +/* + * @tc.name: ComplexData 002 + * @tc.desc: Verify that can batch and single crud continuously. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, ComplexData002, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries12; + entries12.push_back(ENTRY_1_2); + entries12.push_back(ENTRY_2_3); + vector entries23; + entries23.push_back(ENTRY_2); + entries23.push_back(ENTRY_3); + vector keys23; + keys23.push_back(ENTRY_2.key); + keys23.push_back(ENTRY_3.key); + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + EXPECT_TRUE(PutBatchTwoRecords(entries1)); + + /** + * @tc.steps: step2. delete k1 and then get. + * @tc.expected: step2. delete successfully get null of k1,v2 of k2. + */ + DBStatus status2 = DistributedTestTools::Delete(*g_kvStoreBatchDelegate, KEY_1); + ASSERT_TRUE(status2 == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + valueResult = DistributedTestTools::Get(*g_kvStoreBatchDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + CheckBatchCrud(keys23); +} + +vector MultiSnapCheck1(vector &entries1, vector &entries2, DistributedDB::Key &keyPrefix, + KvStoreObserverSnapImpl &observer3, KvStoreObserverSnapImpl &observer4) +{ + /** + * @tc.steps: step3. Register snap2 then getEntries with keyPrefix="k". + * @tc.expected: step3. operate successfully and getEntries are null. + */ + KvStoreObserverSnapImpl observer2; + KvStoreSnapshotCallback kvStoreSnapshotCallback2; + function&)> function2 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback2, _1, _2); + KvStoreSnapshotDelegate *snap2 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer2); + EXPECT_NE(snap2, nullptr); + snap2->GetEntries(keyPrefix, function2); + vector resultEntries2 = kvStoreSnapshotCallback2.GetEntries(); + vector resultEmptyEntry; + EXPECT_TRUE(CompareEntriesVector(resultEntries2, resultEmptyEntry)); + /** + * @tc.steps: step4. putBatch (k1,v1)(k2,v2) again and Register snap3 then getEntries with keyPrefix="k". + * @tc.expected: step4. operate successfully and getEntries are (k1,v1)(k2,v2). + */ + vector entriesInDB; + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + for (auto eachEntry : entries1) { + entriesInDB.push_back(eachEntry); + } + KvStoreSnapshotCallback kvStoreSnapshotCallback3; + function&)> function3 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback3, _1, _2); + KvStoreSnapshotDelegate *snap3 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer3); + EXPECT_NE(snap3, nullptr); + snap3->GetEntries(keyPrefix, function3); + vector resultEntries3 = kvStoreSnapshotCallback3.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries3, entries1)); + /** + * @tc.steps: step5. putBatch (k1,v2)(k2,v3) again, Register snap4 then getEntries with keyPrefix="k". + * @tc.expected: step5. operate successfully and getEntries are (k1,v2)(k2,v3). + */ + status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries2); + for (auto eachEntry : entries2) { + PutUniqueKey(entriesInDB, eachEntry.key, eachEntry.value); + } + KvStoreSnapshotCallback kvStoreSnapshotCallback4; + function&)> function4 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback4, _1, _2); + KvStoreSnapshotDelegate *snap4 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer4); + EXPECT_NE(snap4, nullptr); + snap4->GetEntries(keyPrefix, function4); + vector resultEntries4 = kvStoreSnapshotCallback4.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries4, entries2)); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap2); + EXPECT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap3); + EXPECT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap4); + EXPECT_TRUE(status == DBStatus::OK); + return entriesInDB; +} + +void SnapWithTransactionCheck(vector keys1, DistributedDB::Key keyPrefix, + KvStoreObserverSnapImpl observer5, vector entriesInDB) +{ + /** + * @tc.steps: step6. StartTransaction then deleteBatch (k1,v2)(k2,v3). + * @tc.expected: step6. operate successfully to construct there is no data in db. + */ + KvStoreSnapshotCallback kvStoreSnapshotCallback5; + function&)> function5 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback5, _1, _2); + + DBStatus statusStart = g_kvStoreBatchDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus status = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1); + ASSERT_TRUE(status == DBStatus::OK); + for (unsigned long idxKey = 0; idxKey < keys1.size(); ++idxKey) { + for (unsigned long idxEntry = 0; idxEntry < entriesInDB.size(); ++idxEntry) { + if (CompareVector(keys1[idxKey], entriesInDB[idxEntry].key)) { + entriesInDB.erase(entriesInDB.begin() + idxEntry); + } + } + } + /** + * @tc.steps: step7. put(k1,v1)(k2,v3) alone and commit, Register snap5 to getEntries with prefix-key='k'. + * @tc.expected: step7. operate successfully and getEntries are (k1,v1)(k2,v3). + */ + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + PutUniqueKey(entriesInDB, KEY_1, VALUE_1); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, KEY_2, VALUE_2); + ASSERT_TRUE(status == DBStatus::OK); + PutUniqueKey(entriesInDB, KEY_2, VALUE_2); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, KEY_3, VALUE_3); + ASSERT_TRUE(status == DBStatus::OK); + PutUniqueKey(entriesInDB, KEY_3, VALUE_3); + status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, KEY_2, VALUE_3); + ASSERT_TRUE(status == DBStatus::OK); + PutUniqueKey(entriesInDB, KEY_2, VALUE_3); + status = DistributedTestTools::Delete(*g_kvStoreBatchDelegate, KEY_3); + ASSERT_TRUE(status == DBStatus::OK); + for (unsigned long idx = 0; idx < entriesInDB.size(); ++idx) { + if (CompareVector(entriesInDB[idx].key, KEY_3)) { + entriesInDB.erase(entriesInDB.begin() + idx); + } + } + DBStatus statusCommit = g_kvStoreBatchDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + KvStoreSnapshotDelegate *snap5 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer5); + EXPECT_NE(snap5, nullptr); + snap5->GetEntries(keyPrefix, function5); + vector resultEntries5 = kvStoreSnapshotCallback5.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries5, entriesInDB)); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap5); + ASSERT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: ComplexSnap 001 + * @tc.desc: Verify that can read multiple snapshots correctly after crud repeatedly. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, ComplexSnap001, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries2; + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_2_3); + KvStoreObserverSnapImpl observer1; + KvStoreObserverSnapImpl observer3; + KvStoreObserverSnapImpl observer4; + KvStoreObserverSnapImpl observer5; + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step1. putBatch successfully to construct data in db. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries1); + ASSERT_TRUE(status == DBStatus::OK); + KvStoreSnapshotCallback kvStoreSnapshotCallback; + function&)> function1 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback, _1, _2); + /** + * @tc.steps: step2. Register snap1 then getEntries with keyPrefix="k" and then delete. + * @tc.expected: step2. operate successfully and getEntries are (k1,v1)(k2,v2). + */ + KvStoreSnapshotDelegate *snap1 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer1); + EXPECT_NE(snap1, nullptr); + vector keys1; + for (unsigned long time = 0; time < entries1.size(); ++time) { + keys1.push_back(entries1.at(time).key); + } + DistributedDB::Key keyPrefix = { 'k' }; + snap1->GetEntries(keyPrefix, function1); + vector resultEntries = kvStoreSnapshotCallback.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries, entries1)); + status = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1); + ASSERT_TRUE(status == DBStatus::OK); + vector entriesInDB = MultiSnapCheck1(entries1, entries2, keyPrefix, observer3, observer4); + SnapWithTransactionCheck(keys1, keyPrefix, observer5, entriesInDB); + + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap1); + ASSERT_TRUE(status == DBStatus::OK); + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); +} + +void RegisterSnapAgainAndCheck1(KvStoreObserverSnapImpl &observer1, KvStoreSnapshotDelegate *&snap1, + vector &entries, DistributedDB::Key &keyPrefix) +{ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries); + ASSERT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step2. ReleaseKvStoreSnapshot then RegisterSnapObserver snap1 and getEntries with keyPrefix="k". + * @tc.expected: step2. operate successfully and getEntries are (k1,v1)(k2,v2). + */ + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap1); + ASSERT_TRUE(status == DBStatus::OK); + snap1 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer1); + EXPECT_NE(snap1, nullptr); + KvStoreSnapshotCallback kvStoreSnapshotCallback1; + function&)> function1 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback1, _1, _2); + + snap1->GetEntries(keyPrefix, function1); + vector resultEntries = kvStoreSnapshotCallback1.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries, entries)); +} + +vector RegisterSnapAgainAndCheck2(KvStoreObserverSnapImpl &observer2, KvStoreSnapshotDelegate *&snap2, + vector &entries, DistributedDB::Key &keyPrefix) +{ + /** + * @tc.steps: step3. deleteBatch and RegisterSnapObserver snap2 then getEntries with keyPrefix="k". + * @tc.expected: step3. operate successfully and getEntries are null. + */ + vector keys1; + for (unsigned long time = 0; time < entries.size(); ++time) { + keys1.push_back(entries.at(time).key); + } + DBStatus status = DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1); + EXPECT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap2); + EXPECT_TRUE(status == DBStatus::OK); + snap2 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer2); + EXPECT_NE(snap2, nullptr); + KvStoreSnapshotCallback kvStoreSnapshotCallback2; + function&)> function2 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback2, _1, _2); + snap2->GetEntries(keyPrefix, function2); + vector resultEntries2 = kvStoreSnapshotCallback2.GetEntries(); + vector resultEmptyEntry; + EXPECT_TRUE(CompareEntriesVector(resultEntries2, resultEmptyEntry)); + return keys1; +} + +vector RegisterSnapAgainAndCheck3(KvStoreObserverSnapImpl &observer3, KvStoreSnapshotDelegate *&snap3, + vector &entries, DistributedDB::Key &keyPrefix) +{ + /** + * @tc.steps: step4. putBatch (k1,v1)(k2,v2) again and RegisterSnapObserver snap3 then getEntries with keyPrefix="k" + * @tc.expected: step4. operate successfully and getEntries are (k1,v1)(k2,v2). + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries); + EXPECT_TRUE(status == DBStatus::OK); + vector entriesInDB; + for (auto eachEntry : entries) { + entriesInDB.push_back(eachEntry); + } + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap3); + EXPECT_TRUE(status == DBStatus::OK); + snap3 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer3); + EXPECT_NE(snap3, nullptr); + KvStoreSnapshotCallback kvStoreSnapshotCallback3; + function&)> function3 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback3, _1, _2); + snap3->GetEntries(keyPrefix, function3); + vector resultEntries3 = kvStoreSnapshotCallback3.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries3, entries)); + return entriesInDB; +} + +void RegisterSnapAgainAndCheck4(KvStoreObserverSnapImpl &observer4, KvStoreSnapshotDelegate *&snap4, + vector &entries, DistributedDB::Key &keyPrefix, vector &entriesInDB) +{ + /** + * @tc.steps: step5. putBatch (k1,v2)(k2,v3) again and RegisterSnapObserver snap4 then getEntries with keyPrefix="k" + * @tc.expected: step5. operate successfully and getEntries are (k1,v2)(k2,v3). + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreBatchDelegate, entries); + ASSERT_TRUE(status == DBStatus::OK); + for (auto eachEntry : entries) { + PutUniqueKey(entriesInDB, eachEntry.key, eachEntry.value); + } + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap4); + ASSERT_TRUE(status == DBStatus::OK); + snap4 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer4); + EXPECT_NE(snap4, nullptr); + KvStoreSnapshotCallback kvStoreSnapshotCallback4; + function&)> function4 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback4, _1, _2); + snap4->GetEntries(keyPrefix, function4); + vector resultEntries4 = kvStoreSnapshotCallback4.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries4, entries)); +} + +void PutAndCheck(DistributedDB::Key KEY_1, DistributedDB::Value VALUE_1, vector &entriesInDB) +{ + DBStatus status = DistributedTestTools::Put(*g_kvStoreBatchDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + PutUniqueKey(entriesInDB, KEY_1, VALUE_1); +} + +void RegisterSnapAgainAndCheck5(KvStoreObserverSnapImpl &observer5, KvStoreSnapshotDelegate *&snap5, + vector &entries1, DistributedDB::Key &keyPrefix, vector &entriesInDB) +{ + vector keys1; + for (unsigned long time = 0; time < entries1.size(); ++time) { + keys1.push_back(entries1.at(time).key); + } + /** + * @tc.steps: step6. StartTransaction then deleteBatch (k1,v2)(k2,v3). + * @tc.expected: step6. operate successfully to construct there is no data in db. + */ + ASSERT_TRUE((g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap5)) == OK); + snap5 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer5); + EXPECT_NE(snap5, nullptr); + KvStoreSnapshotCallback kvStoreSnapshotCallback5; + function&)> function5 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback5, _1, _2); + EXPECT_TRUE((g_kvStoreBatchDelegate->StartTransaction()) == OK); + ASSERT_TRUE((DistributedTestTools::DeleteBatch(*g_kvStoreBatchDelegate, keys1)) == OK); + for (unsigned long idxKey = 0; idxKey < keys1.size(); ++idxKey) { + for (unsigned long idxEntry = 0; idxEntry < entriesInDB.size(); ++idxEntry) { + if (CompareVector(keys1[idxKey], entriesInDB[idxEntry].key)) { + entriesInDB.erase(entriesInDB.begin() + idxEntry); + } + } + } + /** + * @tc.steps: step7. put(k1,v1)(k2,v3) alone and commit, Register snap5 to getEntries with prefix-key='k'. + * @tc.expected: step7. operate successfully and getEntries are (k1,v1)(k2,v3). + */ + PutAndCheck(KEY_1, VALUE_1, entriesInDB); + PutAndCheck(KEY_2, VALUE_2, entriesInDB); + PutAndCheck(KEY_3, VALUE_3, entriesInDB); + PutAndCheck(KEY_2, VALUE_3, entriesInDB); + ASSERT_TRUE((DistributedTestTools::Delete(*g_kvStoreBatchDelegate, KEY_3)) == OK); + for (unsigned long idx = 0; idx < entriesInDB.size(); ++idx) { + if (CompareVector(entriesInDB[idx].key, KEY_3)) { + entriesInDB.erase(entriesInDB.begin() + idx); + } + } + EXPECT_TRUE((g_kvStoreBatchDelegate->Commit()) == OK); + ASSERT_TRUE((g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap5)) == OK); + snap5 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer5); + EXPECT_NE(snap5, nullptr); + snap5->GetEntries(keyPrefix, function5); + vector resultEntries5 = kvStoreSnapshotCallback5.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries5, entriesInDB)); +} + +void RegisterSnapAgainAndCheck6(KvStoreSnapshotDelegate *snap6, KvStoreSnapshotDelegate *snap7, + KvStoreSnapshotDelegate *snap8, DistributedDB::Key &keyPrefix) +{ + /** + * @tc.steps: step8. RegisterSnapObserver snap6,snap7,snap8 to getEntries with prefix-key='k'. + * @tc.expected: step8. getEntries are empty. + */ + KvStoreSnapshotCallback kvStoreSnapshotCallback6; + function&)> function6 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback6, _1, _2); + snap6->GetEntries(keyPrefix, function6); + vector resultEntries6 = kvStoreSnapshotCallback6.GetEntries(); + vector resultEmptyEntry; + EXPECT_TRUE(CompareEntriesVector(resultEntries6, resultEmptyEntry)); + KvStoreSnapshotCallback kvStoreSnapshotCallback7; + function&)> function7 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback7, _1, _2); + snap7->GetEntries(keyPrefix, function7); + vector resultEntries7 = kvStoreSnapshotCallback7.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries7, resultEmptyEntry)); + KvStoreSnapshotCallback kvStoreSnapshotCallback8; + function&)> function8 + = bind(&KvStoreSnapshotCallback::Callback, &kvStoreSnapshotCallback8, _1, _2); + snap8->GetEntries(keyPrefix, function8); + vector resultEntries8 = kvStoreSnapshotCallback8.GetEntries(); + EXPECT_TRUE(CompareEntriesVector(resultEntries8, resultEmptyEntry)); +} + +void ReleaseSnap(KvStoreSnapshotDelegate *&snap1, KvStoreSnapshotDelegate *&snap2, + KvStoreSnapshotDelegate *&snap3, KvStoreSnapshotDelegate *&snap4) +{ + DBStatus status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap1); + ASSERT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap2); + ASSERT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap3); + ASSERT_TRUE(status == DBStatus::OK); + status = g_kvStoreBatchDelegate->ReleaseKvStoreSnapshot(snap4); + ASSERT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: ComplexSnap 002 + * @tc.desc: Verify that release the registered snapshot, register again successfully. + * @tc.type: FUN + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvBatchCrudTest, ComplexSnap002, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries12; + entries12.push_back(ENTRY_1_2); + entries12.push_back(ENTRY_2_3); + KvStoreObserverSnapImpl observer1, observer2, observer3, observer4, observer5, observer6, observer7, observer8; + KvStoreSnapshotDelegate *snap1 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer1); + EXPECT_NE(snap1, nullptr); + KvStoreSnapshotDelegate *snap2 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer2); + EXPECT_NE(snap2, nullptr); + KvStoreSnapshotDelegate *snap3 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer3); + EXPECT_NE(snap3, nullptr); + KvStoreSnapshotDelegate *snap4 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer4); + EXPECT_NE(snap4, nullptr); + KvStoreSnapshotDelegate *snap5 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer5); + EXPECT_NE(snap5, nullptr); + KvStoreSnapshotDelegate *snap6 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer6); + EXPECT_NE(snap6, nullptr); + KvStoreSnapshotDelegate *snap7 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer7); + EXPECT_NE(snap7, nullptr); + KvStoreSnapshotDelegate *snap8 = DistributedTestTools::RegisterSnapObserver(g_kvStoreBatchDelegate, &observer8); + EXPECT_NE(snap8, nullptr); + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step1. putBatch successfully to construct data in db. + */ + DistributedDB::Key keyPrefix = { 'k' }; + RegisterSnapAgainAndCheck1(observer1, snap1, entries1, keyPrefix); + vector keys1 = RegisterSnapAgainAndCheck2(observer2, snap2, entries1, keyPrefix); + vector entriesInDB = RegisterSnapAgainAndCheck3(observer3, snap3, entries1, keyPrefix); + RegisterSnapAgainAndCheck4(observer4, snap4, entries1, keyPrefix, entriesInDB); + RegisterSnapAgainAndCheck5(observer5, snap5, entries1, keyPrefix, entriesInDB); + RegisterSnapAgainAndCheck6(snap6, snap7, snap8, keyPrefix); + ReleaseSnap(snap1, snap2, snap3, snap4); + ReleaseSnap(snap5, snap6, snap7, snap8); + DistributedTestTools::Clear(*g_kvStoreBatchDelegate); +} +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_concurrency_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_concurrency_crud_test.cpp new file mode 100755 index 000000000..72dbc7397 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_concurrency_crud_test.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvConcurrencyCrud { +const bool IS_LOCAL = false; +const unsigned int READ_RECORDS_NUM_START = 1; +const unsigned int READ_RECORDS_NUM_END = 128; +const unsigned int WRITE_RECORDS_NUM_START = 1; +const unsigned int WRITE_RECORDS_NUM_END = 9999; +const unsigned long LONG_TIME_TEST_SECONDS = 10; +const unsigned long LONG_TIME_INTERVAL_MILLSECONDS = 5; + +KvStoreDelegate *g_kvStoreConcurDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_concurManager = nullptr; + +enum ReadOrWriteTag { + READ = 0, + WRITE = 1, + DELETE = 2 +}; + +struct ConcurParam { + unsigned int threadId_; + ReadOrWriteTag tag_; + DistributedDB::Entry* entryPtr_; +}; + +// the thread runnnig methods +void ConcurOperThread(ConcurParam* args) +{ + auto paramsPtr = static_cast(args); + DBStatus status; + Value valueResult; + string strKey, strValue; + + if (paramsPtr->tag_ == READ) { + valueResult = DistributedTestTools::Get(*g_kvStoreConcurDelegate, paramsPtr->entryPtr_->key); + Uint8VecToString(paramsPtr->entryPtr_->value, strValue); + if (valueResult.size() != 0) { + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } + } else if (paramsPtr->tag_ == WRITE) { + status = DistributedTestTools::Put(*g_kvStoreConcurDelegate, + paramsPtr->entryPtr_->key, paramsPtr->entryPtr_->value); + ASSERT_EQ(status, DBStatus::OK); + Uint8VecToString(paramsPtr->entryPtr_->key, strKey); + Uint8VecToString(paramsPtr->entryPtr_->value, strValue); + + valueResult = DistributedTestTools::Get(*g_kvStoreConcurDelegate, paramsPtr->entryPtr_->key); + if (valueResult.size() != 0) { + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } + } else { + valueResult = DistributedTestTools::Get(*g_kvStoreConcurDelegate, paramsPtr->entryPtr_->key); + if (valueResult.size() != 0) { + status = DistributedTestTools::Delete(*g_kvStoreConcurDelegate, paramsPtr->entryPtr_->key); + ASSERT_TRUE(status == DBStatus::NOT_FOUND || status == OK); + + Uint8VecToString(paramsPtr->entryPtr_->key, strKey); + Uint8VecToString(paramsPtr->entryPtr_->value, strValue); + } else { + status = DistributedTestTools::Delete(*g_kvStoreConcurDelegate, paramsPtr->entryPtr_->key); + ASSERT_TRUE(status == DBStatus::NOT_FOUND || status == OK); + } + } +} + +double KvCalculateTime(ConcurParam *&kvThreadParam, const Entry entryCurrent, const SysTime &start, SysDurTime dur) +{ + SysTime end; + kvThreadParam->entryPtr_->key = entryCurrent.key; + kvThreadParam->entryPtr_->value = entryCurrent.value; + std::thread thread = std::thread(ConcurOperThread, reinterpret_cast(kvThreadParam)); + thread.join(); + + std::this_thread::sleep_for(std::chrono::duration(LONG_TIME_INTERVAL_MILLSECONDS)); + end = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + dur = std::chrono::duration_cast(end - start); + double operInterval = static_cast(dur.count()) * chrono::microseconds::period::num + / chrono::microseconds::period::den; + delete kvThreadParam->entryPtr_; + kvThreadParam->entryPtr_ = nullptr; + delete kvThreadParam; + kvThreadParam = nullptr; + return operInterval; +} + +class DistributeddbKvConcurrencyCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbKvConcurrencyCrudTest::SetUpTestCase(void) +{ +} + +void DistributeddbKvConcurrencyCrudTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvConcurrencyCrudTest::SetUp(void) +{ + MST_LOG("SetUpTest before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_kvStoreConcurDelegate = DistributedTestTools::GetDelegateSuccess(g_concurManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_concurManager != nullptr && g_kvStoreConcurDelegate != nullptr); +} + +void DistributeddbKvConcurrencyCrudTest::TearDown(void) +{ + MST_LOG("TearDownTest after all cases."); + EXPECT_TRUE(g_concurManager->CloseKvStore(g_kvStoreConcurDelegate) == OK); + g_kvStoreConcurDelegate = nullptr; + DBStatus status = g_concurManager->DeleteKvStore(STORE_ID_1); + EXPECT_TRUE(status == DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_concurManager; + g_concurManager = nullptr; + RemoveDir(DIRECTOR); +} + +/* + * @tc.name: Read 002 + * @tc.desc: Verify that support long-term multi-threaded concurrent reading. + * @tc.type: Long time + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvConcurrencyCrudTest, Read002, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); + + vector entriesBatch; + vector allKeys; + GenerateRecords(BATCH_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + /** + * @tc.steps: step1. putBatch 128 items of (keys,values) then getEntries with keyprefix='k'. + * @tc.expected: step1. putBatch successfully and the size of GetEntries is 128. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreConcurDelegate, entriesBatch); + EXPECT_TRUE(status == DBStatus::OK); + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreConcurDelegate, KEY_SEARCH_4); + + /** + * @tc.steps: step2. create 6 threads and read data from db concurrently for 10s. + * @tc.expected: step2. read successfully and has no exception. + */ + std::random_device randDevReadKeyNo; + std::mt19937 genRandReadKeyNo(randDevReadKeyNo()); + std::uniform_int_distribution disRandReadKeyNo(READ_RECORDS_NUM_START, READ_RECORDS_NUM_END); + unsigned long randKeyNo; + unsigned int threadCurId = 0; + + SysTime start; + SysDurTime dur; + double operInterval = 0.0; + + start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + Entry entryCurrent; + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + auto threadParam = new (std::nothrow) ConcurParam; + ASSERT_NE(threadParam, nullptr); + threadParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(threadParam->entryPtr_, nullptr); + threadParam->threadId_ = threadCurId++; + threadParam->tag_ = READ; + randKeyNo = disRandReadKeyNo(genRandReadKeyNo); + + GenerateRecord(randKeyNo, entryCurrent); + operInterval = KvCalculateTime(threadParam, entryCurrent, start, dur); + } + + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); +} + +void StartRandThread() +{ + std::vector threads; + std::random_device randDevReadKeyNo, randDevWriteKeyNo, randDevTag; + std::mt19937 genRandReadKeyNo(randDevReadKeyNo()), genRandWriteKeyNo(randDevWriteKeyNo()), genRandTag(randDevTag()); + std::uniform_int_distribution disRandReadKeyNo(READ_RECORDS_NUM_START, READ_RECORDS_NUM_END); + std::uniform_int_distribution disRandWriteKeyNo(WRITE_RECORDS_NUM_START, WRITE_RECORDS_NUM_END); + std::uniform_int_distribution disRandTag(READ, WRITE); + unsigned long randKeyNo; + unsigned int threadCurId = 0; + int randTag; + SysTime start; + SysDurTime dur; + double operInterval = 0.0; + start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + ConcurParam *threadParam = new (std::nothrow) ConcurParam; + ASSERT_NE(threadParam, nullptr); + threadParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(threadParam->entryPtr_, nullptr); + threadParam->threadId_ = threadCurId++; + randTag = disRandTag(genRandTag); + threadParam->tag_ = static_cast(randTag); + if (randTag == READ) { + randKeyNo = disRandReadKeyNo(genRandReadKeyNo); + } else { + randKeyNo = disRandWriteKeyNo(genRandWriteKeyNo); + } + Entry entryCurrent; + GenerateRecord(randKeyNo, entryCurrent); + operInterval = KvCalculateTime(threadParam, entryCurrent, start, dur); + } +} +/* + * @tc.name: ReadWrite 002 + * @tc.desc: Verify that support long-term multi-threaded concurrent reading and writing. + * @tc.type: Long time + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvConcurrencyCrudTest, ReadWrite002, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); + + vector entriesBatch; + vector allKeys; + GenerateRecords(BATCH_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + /** + * @tc.steps: step1. putBatch 128 items of (keys,values) then getEntries with keyprefix='k'. + * @tc.expected: step1. putBatch successfully and the size of GetEntries is 128. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreConcurDelegate, entriesBatch); + EXPECT_TRUE(status == DBStatus::OK); + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreConcurDelegate, KEY_SEARCH_4); + MST_LOG("value size %zu", valueResult.size()); + + /** + * @tc.steps: step2. create 6 threads, read and write data concurrently for 10s. + * @tc.expected: step2. read and write successfully and has no exception. + */ + StartRandThread(); + + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); +} + +/* + * @tc.name: ReadWrite 004 + * @tc.desc: Verify that support multi-threaded concurrent writing. + * @tc.type: Long time + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvConcurrencyCrudTest, ReadWrite004, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); + + vector entriesBatch; + vector allKeys; + GenerateRecords(BATCH_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + /** + * @tc.steps: step1. putBatch 128 items of (keys,values) then getEntries with keyprefix='k'. + * @tc.expected: step1. putBatch successfully and the size of GetEntries is 128. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_kvStoreConcurDelegate, entriesBatch); + EXPECT_TRUE(status == DBStatus::OK); + vector valueResult = DistributedTestTools::GetEntries(*g_kvStoreConcurDelegate, KEY_SEARCH_4); + MST_LOG("value size %zu", valueResult.size()); + /** + * @tc.steps: step2. create 6 threads to write and create 4 threads to delete data concurrently for 10s. + * @tc.expected: step2. write and delete successfully and has no exception. + */ + StartRandThread(); + + DistributedTestTools::Clear(*g_kvStoreConcurDelegate); +} +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_create_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_create_test.cpp new file mode 100755 index 000000000..c546abef1 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_create_test.cpp @@ -0,0 +1,1977 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" +#include "distributed_test_sysinfo.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvCreate { +const int OPER_CNT = 4; +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +class DistributeddbKvCreateTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributeddbKvCreateTest::SetUpTestCase(void) +{ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); +} + +void DistributeddbKvCreateTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvCreateTest::SetUp(void) +{ + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbKvCreateTest::TearDown(void) +{ +} + +/* + * @tc.name: Open 001 + * @tc.desc: Verify that the kv db was created successfully. + * @tc.type: FUNC + * @tc.require: SR000CQS3O + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Open001, TestSize.Level1) +{ + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvStoreDelegateManager *manager3 = nullptr; + KvStoreDelegateManager *manager4 = nullptr; + + /** + * @tc.steps: step1. create kv db with params storeId1, appId1, userId1. + * @tc.expected: step1. the kv db was created successfully. + */ + KvStoreDelegate *result = nullptr; + result = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && result != nullptr); + EXPECT_TRUE(manager1->CloseKvStore(result) == OK); + result = nullptr; + /** + * @tc.steps: step2. create kv db with params storeId2, appId1, userId1. + * @tc.expected: step2. the kv db was created successfully. + */ + result = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(manager2 != nullptr && result != nullptr); + EXPECT_TRUE(manager2->CloseKvStore(result) == OK); + result = nullptr; + /** + * @tc.steps: step3. create kv db with params storeId1, appId2, userId1. + * @tc.expected: step3. the kv db was created successfully. + */ + result = DistributedTestTools::GetDelegateSuccess(manager3, g_kvdbParameter1_2_1, g_kvOption); + ASSERT_TRUE(manager3 != nullptr && result != nullptr); + EXPECT_TRUE(manager3->CloseKvStore(result) == OK); + result = nullptr; + /** + * @tc.steps: step4. create kv db with params storeId1, appId1, userId2. + * @tc.expected: step4. the kv db was created successfully. + */ + result = DistributedTestTools::GetDelegateSuccess(manager4, g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager4 != nullptr && result != nullptr); + EXPECT_EQ(manager4->CloseKvStore(result), OK); + result = nullptr; + + EXPECT_EQ(manager1->DeleteKvStore(STORE_ID_1), OK); + delete manager1; + manager1 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; + EXPECT_EQ(manager3->DeleteKvStore(STORE_ID_1), OK); + delete manager3; + manager3 = nullptr; + EXPECT_EQ(manager4->DeleteKvStore(STORE_ID_1), OK); + delete manager4; + manager4 = nullptr; +} + +/* + * @tc.name: Open 002 + * @tc.desc: Verify that the kv db can be reopened successfully + * @tc.type: FUNC + * @tc.require: SR000CQS3O + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Open002, TestSize.Level0) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create kv db. + * @tc.expected: step1. the kv db was created successfully. + */ + KvOption option = g_kvOption; + option.localOnly = IS_LOCAL_ONLY; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + delete manager; + manager = nullptr; + /** + * @tc.steps: step2. reopen kv db. + * @tc.expected: step2. the kv db was reopened successfully. + */ + option.createIfNecessary = IS_NOT_NEED_CREATE; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Open 003 + * @tc.desc: Verify that can not reopen an absent db. + * @tc.type: FUNC + * @tc.require: SR000CQS3O + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Open003, TestSize.Level0) +{ + /** + * @tc.steps: step1. delete kv db if exist. + * @tc.expected: step1. Construct that no database exist. + */ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + SetDir(DIRECTOR); + DBStatus status = manager->SetKvStoreConfig(KV_CONFIG); + EXPECT_EQ(status, DBStatus::OK); + status = manager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, DBStatus::NOT_FOUND); + delete manager; + manager = nullptr; + + KvStoreDelegate *delegate = nullptr; + /** + * @tc.steps: step2. reopen an absent db. + * @tc.expected: step2. reopen failed and return error. + */ + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + option.localOnly = IS_LOCAL_ONLY; + status = DistributedTestTools::GetDelegateNotGood(manager, delegate, STORE_ID_1, APP_ID_1, USER_ID_1, option); + ASSERT_NE(status, DBStatus::OK); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Open 004 + * @tc.desc: Verify that transferring param IS_NOT_NEED_CREATE can not create kv db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Open004, TestSize.Level1) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_2); + ASSERT_NE(manager, nullptr); + SetDir(DIRECTOR); + EXPECT_EQ(manager->SetKvStoreConfig(KV_CONFIG), DBStatus::OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), DBStatus::NOT_FOUND); + + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + /** + * @tc.steps: step1. create kv db with param IS_NOT_NEED_CREATE. + * @tc.expected: step1. create kv db and close it failed. + */ + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + KvStoreDelegate *result1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter1_1_2, option); + EXPECT_TRUE(manager1 == nullptr && result1 == nullptr); + EXPECT_TRUE(manager->CloseKvStore(result1) != OK); + delete manager; + manager = nullptr; + + /** + * @tc.steps: step2. create kv db with param IS_NEED_CREATE. + * @tc.expected: step2. create kv db and close it successfully. + */ + KvStoreDelegate *result2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter1_1_2, + g_kvOption); + ASSERT_TRUE(manager2 != nullptr && result2 != nullptr); + EXPECT_TRUE(manager2->CloseKvStore(result2) == OK); + result2 = nullptr; + EXPECT_TRUE(manager2->DeleteKvStore(STORE_ID_1) == OK); + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: Open 005 + * @tc.desc: Verify that can not create or open kv db with exceptional params. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Open005, TestSize.Level1) +{ + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvStoreDelegateManager *manager3 = nullptr; + KvStoreDelegateManager *manager4 = nullptr; + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegate *delegate3 = nullptr; + KvStoreDelegate *delegate4 = nullptr; + + /** + * @tc.steps: step1. create kv db with param storeId is null. + * @tc.expected: step1. create kv db failed. + */ + KvOption option1 = g_kvOption; + option1.createIfNecessary = IS_NEED_CREATE; + option1.localOnly = IS_LOCAL_ONLY; + DBStatus status = DistributedTestTools::GetDelegateNotGood(manager1, delegate1, "", APP_ID_1, USER_ID_1, option1); + ASSERT_EQ(status, DBStatus::INVALID_ARGS); + EXPECT_TRUE(delegate1 == nullptr); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == INVALID_ARGS); + + /** + * @tc.steps: step2. create kv db with param appId is null. + * @tc.expected: step2. create kv db failed. + */ + KvOption option2 = g_kvOption; + option2.createIfNecessary = IS_NEED_CREATE; + option2.localOnly = IS_LOCAL_ONLY; + status = DistributedTestTools::GetDelegateNotGood(manager2, delegate2, STORE_ID_1, "", USER_ID_1, option2); + ASSERT_EQ(status, DBStatus::INVALID_ARGS); + EXPECT_TRUE(delegate2 == nullptr); + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == INVALID_ARGS); + + /** + * @tc.steps: step3. create kv db with param userId is null. + * @tc.expected: step3. create kv db failed. + */ + KvOption option3 = g_kvOption; + option3.createIfNecessary = IS_NEED_CREATE; + option3.localOnly = IS_LOCAL_ONLY; + status = DistributedTestTools::GetDelegateNotGood(manager3, delegate3, STORE_ID_1, APP_ID_1, "", option3); + ASSERT_EQ(status, DBStatus::INVALID_ARGS); + EXPECT_TRUE(delegate3 == nullptr); + EXPECT_TRUE(manager3->CloseKvStore(delegate3) == INVALID_ARGS); + + /** + * @tc.steps: step3. create kv db with param userId is null. + * @tc.expected: step3. create kv db failed. + */ + KvOption option4 = g_kvOption; + option4.createIfNecessary = IS_NOT_NEED_CREATE; + option4.localOnly = IS_LOCAL_ONLY; + status = DistributedTestTools::GetDelegateNotGood(manager4, delegate4, STORE_ID_1, APP_ID_1, USER_ID_1, option4); + ASSERT_NE(status, DBStatus::OK); + EXPECT_TRUE(delegate4 == nullptr); + EXPECT_TRUE(manager4->CloseKvStore(delegate4) == INVALID_ARGS); + + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; + delete manager3; + manager3 = nullptr; + delete manager4; + manager4 = nullptr; +} + +/* + * @tc.name: Close 001 + * @tc.desc: Verify that can close kv db successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Close001, TestSize.Level1) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + + /** + * @tc.steps: step1. create and open kv db. + * @tc.expected: step1. create and open kv db successfully. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step2. close kv db that exist. + * @tc.expected: step2. close kv db successfully. + */ + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Close 002 + * @tc.desc: Verify that can not close absent db successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Close002, TestSize.Level1) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + + /** + * @tc.steps: step1. create and delete kv db. + * @tc.expected: step1. Construct that no database exist. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + + /** + * @tc.steps: step2. close nonexistent db. + * @tc.expected: step2. close db failed. + */ + EXPECT_EQ(manager->CloseKvStore(result), INVALID_ARGS); + + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Delete 001 + * @tc.desc: Verify that can delete db successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Delete001, TestSize.Level1) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + /** + * @tc.steps: step2. delete db. + * @tc.expected: step2. delete db successfully. + */ + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Delete 002 + * @tc.desc: Verify that can not delete absent db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Delete002, TestSize.Level1) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + + /** + * @tc.steps: step1. create and delete kv db. + * @tc.expected: step1. Construct that no database exist. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + + /** + * @tc.steps: step2. delete absent db. + * @tc.expected: step2. delete absent db failed. + */ + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == NOT_FOUND); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Config 001 + * @tc.desc: Verify that can set global config of db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Config001, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + /** + * @tc.steps: step1. set rightly exist global config of db. + * @tc.expected: step1. set successfully. + */ + SetDir(DIRECTOR); + KvStoreConfig config; + config.dataDir = DIRECTOR; + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::OK); + /** + * @tc.steps: step2. set exceptionally not exist global config of db. + * @tc.expected: step2. set failed. + */ + config.dataDir = "/dataDED/"; + EXPECT_EQ(manager->SetKvStoreConfig(config), INVALID_ARGS); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Config 002 + * @tc.desc: Verify that can get the set storeid and check it. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Config002, TestSize.Level1) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create kv db with STORE_ID_1. + * @tc.expected: step1. create db successfully. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step2. get the set storeid of db. + * @tc.expected: step2. get the set storeid successfully and it equals STORE_ID_1. + */ + string storeid = result->GetStoreId(); + ASSERT_STREQ(STORE_ID_1.c_str(), storeid.c_str()); + + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 001 + * @tc.desc: calculate the time of creating db. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance001, TestSize.Level2) +{ + std::chrono::steady_clock::time_point tick, tock; + /** + * @tc.steps: step1. get the time1 and then create kv db. + * @tc.expected: step1. create kv db successfully. + */ + SetDir(DIRECTOR); + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, std::placeholders::_1, std::placeholders::_2); + + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig(KV_CONFIG), DBStatus::OK); + KvStoreDelegate::Option option = DistributedTestTools::TransferKvOptionType(g_kvOption); + + tick = std::chrono::steady_clock::now(); + manager->GetKvStore(STORE_ID_1, option, function); + tock = std::chrono::steady_clock::now(); + + KvStoreDelegate* delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + EXPECT_NE(delegate, nullptr); + /** + * @tc.steps: step2. get the time2 after creating kv db. + * @tc.expected: step2. time2-time1<100s. + */ + std::chrono::duration dur; + dur = std::chrono::duration_cast(tock - tick); + double duration = static_cast(dur.count()); + MST_LOG("the time used for create DB is %f ms", duration); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 002 + * @tc.desc: check the system or cpu storage and power when creating db. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance002, TestSize.Level2) +{ + /** + * @tc.steps: step1. get the system info before creating db. + * @tc.expected: step1. the system or cpu storage and power is normal. + */ + DistributedTestSysInfo si; + MST_LOG("System info before opening db"); + si.GetSysMemOccpy(FIRST); + si.GetSysCpuUsage(FIRST, DEFAULT_INTEVAL); + si.GetSysCurrentPower(FIRST, DEFAULT_COUNT, DEFAULT_INTEVAL); + + /** + * @tc.steps: step2. construct the params for interface and create db + * @tc.expected: step2. create db successfully. + */ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step3. get the system info after creating db. + * @tc.expected: step3. the system or cpu storage and power is normal. + */ + MST_LOG("System info after opening db"); + si.GetSysMemOccpy(SECOND); + si.GetSysCpuUsage(SECOND, DEFAULT_INTEVAL); + si.GetSysCurrentPower(SECOND, DEFAULT_COUNT, DEFAULT_INTEVAL); + + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 003 + * @tc.desc: calculate the time of reopening db. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance003, TestSize.Level2) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + KvOption option = g_kvOption; + option.localOnly = IS_LOCAL_ONLY; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, std::placeholders::_1, std::placeholders::_2); + std::chrono::steady_clock::time_point tick, tock; + option.createIfNecessary = IS_NOT_NEED_CREATE; + /** + * @tc.steps: step2. get the tick and tock value of the system before and after reopen the db. + * @tc.expected: step2. the system time of tick and tock obtained successfully. + */ + KvStoreDelegate::Option optionUsedForGetStore = DistributedTestTools::TransferKvOptionType(option); + tick = std::chrono::steady_clock::now(); + manager->GetKvStore(STORE_ID_1, optionUsedForGetStore, function); + tock = std::chrono::steady_clock::now(); + + /** + * @tc.steps: step3. tock - tick and translate it to milliseconds. + * @tc.expected: step3. tock - tick < 150ms. + */ + std::chrono::duration dur; + dur = std::chrono::duration_cast(tock - tick); + double duration = static_cast(dur.count()); + MST_LOG("the time used for reopen DB is %f ms", duration); + + delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + EXPECT_NE(delegate, nullptr); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 004 + * @tc.desc: check the system or cpu storage and power when reopening db. + * @tc.type: System overhead + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance004, TestSize.Level3) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + KvOption option04 = g_kvOption; + option04.localOnly = IS_LOCAL_ONLY; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option04); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + /** + * @tc.steps: step2. get the system info before reopening db and then reopen db. + * @tc.expected: step2. the system or cpu storage and power is normal and reopen db successful . + */ + MST_LOG("System info before reopening db"); + DistributedTestSysInfo sysInfo04; + sysInfo04.GetSysMemOccpy(FIRST); + sysInfo04.GetSysCpuUsage(FIRST, DEFAULT_INTEVAL); + sysInfo04.GetSysCurrentPower(FIRST, DEFAULT_COUNT, DEFAULT_INTEVAL); + option04.createIfNecessary = IS_NOT_NEED_CREATE; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option04); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step3. get the system info after reopening db. + * @tc.expected: step3. the system or cpu storage and power is normal and reopen db successful. + */ + MST_LOG("System info after reopening db"); + sysInfo04.GetSysMemOccpy(SECOND); + sysInfo04.GetSysCpuUsage(SECOND, DEFAULT_INTEVAL); + sysInfo04.GetSysCurrentPower(SECOND, DEFAULT_COUNT, DEFAULT_INTEVAL); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 005 + * @tc.desc: check the system storage when reopening by reopening db. + * @tc.type: System overhead + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance005, TestSize.Level3) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + KvOption option05 = g_kvOption; + option05.localOnly = IS_LOCAL_ONLY; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option05); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + /** + * @tc.steps: step2. reopen db. + * @tc.expected: step2. reopen db successful. + */ + option05.createIfNecessary = IS_NOT_NEED_CREATE; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option05); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step3. get the system info before close db. + * @tc.expected: step3. reopen db successful. + */ + MST_LOG("System info before close db again and again"); + DistributedTestSysInfo sysInfo05; + sysInfo05.GetSysMemOccpy(FIRST); + sysInfo05.GetSysCpuUsage(FIRST, DEFAULT_INTEVAL); + sysInfo05.GetSysCurrentPower(FIRST, DEFAULT_COUNT, DEFAULT_INTEVAL); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option05); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step4. get the system info after reopening db. + * @tc.expected: step4. the system has no memory leak. + */ + MST_LOG("System info after opening db again and again"); + sysInfo05.GetSysMemOccpy(SECOND); + sysInfo05.GetSysCpuUsage(SECOND, DEFAULT_INTEVAL); + sysInfo05.GetSysCurrentPower(SECOND, DEFAULT_COUNT, DEFAULT_INTEVAL); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Performance 006 + * @tc.desc: check the handler using when reopening by reopening db. + * @tc.type: System overhead + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, Performance006, TestSize.Level2) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + KvOption option06 = g_kvOption; + option06.localOnly = IS_LOCAL_ONLY; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option06); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + /** + * @tc.steps: step2. reopen by reopen db three times. + * @tc.expected: step2. reopen db successful and the system has no handle overflow. + */ + option06.createIfNecessary = IS_NOT_NEED_CREATE; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option06); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + option06.createIfNecessary = IS_NEED_CREATE; + option06.localOnly = IS_NOT_LOCAL_ONLY; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option06); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option06); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 001 + * @tc.desc: check it that can't create kv db without setting path with manager->SetKvStoreConfig(KV_CONFIG) interface. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException001, TestSize.Level2) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *delegate = nullptr; + + SetDir(DIRECTOR); + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, std::placeholders::_1, std::placeholders::_2); + + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + /** + * @tc.steps: step1. create kv db without setting path with manager->SetKvStoreConfig(KV_CONFIG) interface. + * @tc.expected: step1. create and close db failed. + */ + KvStoreDelegate::Option option = DistributedTestTools::TransferKvOptionType(g_kvOption); + manager->GetKvStore(STORE_ID_1, option, function); + EXPECT_EQ(delegateKvMgrCallback.GetStatus(), DBStatus::INVALID_ARGS); + + delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), DBStatus::INVALID_ARGS); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 002 + * @tc.desc: create kv db after setting no existing path. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException002, TestSize.Level2) +{ + /** + * @tc.steps: step1. create kv db when setting no existing path. + * @tc.expected: step1. create db failed. + */ + DelegateKvMgrCallback delegateKvMgrCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateKvMgrCallback, std::placeholders::_1, std::placeholders::_2); + + KvStoreConfig config; + config.dataDir = "/NOTFOUUD/PATH"; + + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + KvStoreDelegate::Option option = {true, true}; + + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::INVALID_ARGS); + + manager->GetKvStore(STORE_ID_1, option, function); + EXPECT_EQ(delegateKvMgrCallback.GetStatus(), INVALID_ARGS); + + KvStoreDelegate* delegate = const_cast(delegateKvMgrCallback.GetKvStore()); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), DBStatus::INVALID_ARGS); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 003 + * @tc.desc: create kv db with invalid-char param. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException003, TestSize.Level2) +{ + char chinese[5] = { static_cast(0xD6), static_cast(0xD0), + static_cast(0xCE), static_cast(0xC4) }; // not good ascii letters: + std::string chStr = chinese; + MST_LOG("%s", chStr.c_str()); + + DelegateKvMgrCallback exceptionCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &exceptionCallback, std::placeholders::_1, std::placeholders::_2); + KvStoreConfig config; + config.dataDir = ""; + /** + * @tc.steps: step1. create kv db with the path is null. + * @tc.expected: step1. create db failed. + */ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + KvStoreDelegate::Option option = {true, true}; + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::INVALID_ARGS); + manager->GetKvStore(STORE_ID_1, option, function); + EXPECT_EQ(exceptionCallback.GetStatus(), INVALID_ARGS); + delete manager; + manager = nullptr; + /** + * @tc.steps: step1. create kv db with storeId="中文". + * @tc.expected: step1. create db failed. + */ + SetDir(DIRECTOR); + config.dataDir = DIRECTOR; + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::OK); + manager->GetKvStore(chStr, option, function); + EXPECT_EQ(exceptionCallback.GetStatus(), INVALID_ARGS); + delete manager; + manager = nullptr; + + /** + * @tc.steps: step2. create kv db with appId="中文". + * @tc.expected: step2. create db failed. + */ + manager = new (std::nothrow) KvStoreDelegateManager(chStr, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::OK); + manager->GetKvStore(chStr, option, function); + EXPECT_EQ(exceptionCallback.GetStatus(), INVALID_ARGS); + delete manager; + manager = nullptr; + /** + * @tc.steps: step3. create kv db with userId="中文". + * @tc.expected: step3. create db failed. + */ + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, chStr); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig(config), DBStatus::OK); + manager->GetKvStore(chStr, option, function); + EXPECT_EQ(exceptionCallback.GetStatus(), INVALID_ARGS); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 004 + * @tc.desc: create kv db with too long param. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException004, TestSize.Level2) +{ + std::string storeTooLongID, appTooLongID, userTooLongID; + storeTooLongID.append(TWO_M_LONG_STRING, 'a'); + appTooLongID.append(TWO_M_LONG_STRING, 'b'); + userTooLongID.append(TWO_M_LONG_STRING, 'c'); + + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvStoreDelegateManager *manager3 = nullptr; + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegate *delegate3 = nullptr; + /** + * @tc.steps: step1. create kv db with storeId=storeTooLongID. + * @tc.expected: step1. create db failed. + */ + DBStatus status1 = DistributedTestTools::GetDelegateNotGood(manager1, delegate1, + storeTooLongID, APP_ID_1, USER_ID_1, g_kvOption); + EXPECT_EQ(delegate1, nullptr); + EXPECT_EQ(manager1->CloseKvStore(delegate1), INVALID_ARGS); + /** + * @tc.steps: step2. create kv db with appId=appTooLongID. + * @tc.expected: step2. create db failed. + */ + DBStatus status2 = DistributedTestTools::GetDelegateNotGood(manager2, delegate2, + USER_ID_1, appTooLongID, USER_ID_1, g_kvOption); + EXPECT_EQ(delegate2, nullptr); + EXPECT_EQ(manager2->CloseKvStore(delegate2), INVALID_ARGS); + /** + * @tc.steps: step3. create kv db with userId=userTooLongID. + * @tc.expected: step3. create db failed. + */ + DBStatus status3 = DistributedTestTools::GetDelegateNotGood(manager3, delegate3, + USER_ID_1, APP_ID_1, userTooLongID, g_kvOption); + EXPECT_EQ(delegate3, nullptr); + EXPECT_EQ(manager3->CloseKvStore(delegate3), INVALID_ARGS); + + ASSERT_EQ(status1, DBStatus::INVALID_ARGS); + ASSERT_EQ(status2, DBStatus::INVALID_ARGS); + ASSERT_EQ(status3, DBStatus::INVALID_ARGS); + + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; + delete manager3; + manager3 = nullptr; +} + +/* + * @tc.name: PathException 005 + * @tc.desc: verify that the same DB can be reopen many times. + * @tc.type: Stability + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException005, TestSize.Level2) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + KvOption option = g_kvOption; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1_1_2, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + + /** + * @tc.steps: step2. reopen kv db five times. + * @tc.expected: step2. reopen successfully and no memory exception when closing db. + */ + option.createIfNecessary = IS_NEED_CREATE; + KvStoreDelegate::Option optionUsedForGetStore = DistributedTestTools::TransferKvOptionType(option); + + DelegateKvMgrCallback delegateCallback; + function function + = bind(&DelegateKvMgrCallback::Callback, &delegateCallback, std::placeholders::_1, std::placeholders::_2); + KvStoreDelegate* delegate = nullptr; + for (int operCnt = 1; operCnt <= OPER_CNT; ++operCnt) { + manager->GetKvStore(STORE_ID_1, optionUsedForGetStore, function); + EXPECT_EQ(delegateCallback.GetStatus(), OK); + delegate = const_cast(delegateCallback.GetKvStore()); + EXPECT_NE(delegate, nullptr); + + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + } + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), DBStatus::OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 006 + * @tc.desc: verify that one delegate can be closed only one time. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException006, TestSize.Level2) +{ + KvStoreDelegate *result = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + + /** + * @tc.steps: step2. create and close kv db more four times. + * @tc.expected: step2. close successfully and no memory leak. + */ + for (int operCnt = 1; operCnt <= OPER_CNT; ++operCnt) { + EXPECT_EQ(manager->CloseKvStore(result), INVALID_ARGS); + } + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: PathException 007 + * @tc.desc: open deleted kv db. + * @tc.type: EXCEPTION + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, PathException007, TestSize.Level2) +{ + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + /** + * @tc.steps: step1. create and close kv db. + * @tc.expected: step1. Construct that database exist. + */ + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + + /** + * @tc.steps: step2. delete the DB. + * @tc.expected: step2. delete successfully. + */ + EXPECT_EQ(manager1->DeleteKvStore(STORE_ID_1), OK); + delete manager1; + manager1 = nullptr; + /** + * @tc.steps: step3. open the deleted DB with the mode createIfNecessary = IS_NOT_NEED_CREATE. + * @tc.expected: step3. open failed and return error. + */ + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + option.localOnly = IS_LOCAL_ONLY; + DBStatus status = DistributedTestTools::GetDelegateNotGood(manager2, delegate2, + STORE_ID_1, APP_ID_1, USER_ID_2, option); + EXPECT_TRUE(delegate2 == nullptr); + EXPECT_EQ(manager2->CloseKvStore(delegate2), INVALID_ARGS); + ASSERT_NE(status, OK); + + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: ConflictConfig 001 + * @tc.desc: check SetConflictResolutionPolicy interface with AUTO_LAST_WIN mode + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, ConflictConfig001, TestSize.Level1) +{ + KvStoreDelegate *conflictDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + conflictDelegate = DistributedTestTools::GetDelegateSuccess(manager, + g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && conflictDelegate != nullptr); + + /** + * @tc.steps: step1. check SetConflictResolutionPolicy interface with AUTO_LAST_WIN mode. + * @tc.expected: step1. return ok. + */ + EXPECT_EQ(conflictDelegate->SetConflictResolutionPolicy(AUTO_LAST_WIN, nullptr), OK); + + EXPECT_TRUE(manager->CloseKvStore(conflictDelegate) == OK); + conflictDelegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ConflictConfig 002 + * @tc.desc: check SetConflictResolutionPolicy interface with CUSTOMER_RESOLUTION mode + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, ConflictConfig002, TestSize.Level1) +{ + KvStoreDelegate *conflictDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + conflictDelegate = DistributedTestTools::GetDelegateSuccess(manager, + g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && conflictDelegate != nullptr); + + /** + * @tc.steps: step1. check SetConflictResolutionPolicy interface with CUSTOMER_RESOLUTION mode. + * @tc.expected: step1. return error. + */ + EXPECT_NE(conflictDelegate->SetConflictResolutionPolicy(CUSTOMER_RESOLUTION, nullptr), OK); + + EXPECT_TRUE(manager->CloseKvStore(conflictDelegate) == OK); + conflictDelegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ConflictConfig 003 + * @tc.desc: check SetConflictResolutionPolicy interface with invalid mode + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCreateTest, ConflictConfig003, TestSize.Level1) +{ + KvStoreDelegate *conflictDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + conflictDelegate = DistributedTestTools::GetDelegateSuccess(manager, + g_kvdbParameter1_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && conflictDelegate != nullptr); + + /** + * @tc.steps: step1. check SetConflictResolutionPolicy interface with invalid mode. + * @tc.expected: step1. return ok. + */ + EXPECT_EQ(conflictDelegate->SetConflictResolutionPolicy(static_cast(2), nullptr), DB_ERROR); + EXPECT_EQ(conflictDelegate->SetConflictResolutionPolicy(static_cast(-1), nullptr), DB_ERROR); + + EXPECT_TRUE(manager->CloseKvStore(conflictDelegate) == OK); + conflictDelegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: OptionParam 001 + * @tc.desc: verify that will check the option parameter when create encrypted DB. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, OptionParam001, TestSize.Level1) +{ + vector manager = {nullptr, nullptr, nullptr}; + vector result = {nullptr, nullptr, nullptr, nullptr, nullptr}; + KvOption option; + vector password; + + /** + * @tc.steps: step1. isEncryptedDb=true, passwd=NULL, cipher=DEFAULT when create db. + * @tc.expected: step1. create failed and return INVALID_ARGS. + */ + option.isEncryptedDb = true; + option.cipher = CipherType::DEFAULT; + option.passwd = NULL_PASSWD_VECTOR; + DBStatus status; + result[INDEX_ZEROTH] = DistributedTestTools::GetDelegateStatus(manager[INDEX_ZEROTH], + status, g_kvdbParameter1, option); + EXPECT_TRUE(manager[INDEX_ZEROTH] == nullptr && result[INDEX_ZEROTH] == nullptr); + EXPECT_TRUE(status == INVALID_ARGS); + + /** + * @tc.steps: step2. isEncryptedDb=true, passwd=a......(100B) when create db. + * @tc.expected: step2. create successfully and return OK. + */ + password.assign(VALUE_ONE_HUNDRED_BYTE, 'a'); + option.passwd = password; + result[INDEX_FIRST] = DistributedTestTools::GetDelegateSuccess(manager[INDEX_ZEROTH], g_kvdbParameter1, option); + ASSERT_TRUE(manager[INDEX_ZEROTH] != nullptr && result[INDEX_FIRST] != nullptr); + EXPECT_EQ(manager[INDEX_ZEROTH]->CloseKvStore(result[INDEX_FIRST]), OK); + result[INDEX_FIRST] = nullptr; + + /** + * @tc.steps: step3. isEncryptedDb=true, passwd=a......(128B), cipher=AES_256_GCM when create db. + * @tc.expected: step3. create successfully and return OK. + */ + password.clear(); + password.assign(BATCH_RECORDS, 'a'); + option.cipher = CipherType::AES_256_GCM; + option.passwd = password; + result[INDEX_SECOND] = DistributedTestTools::GetDelegateSuccess(manager[INDEX_FIRST], g_kvdbParameter2, option); + ASSERT_TRUE(manager[INDEX_FIRST] != nullptr && result[INDEX_SECOND] != nullptr); + EXPECT_EQ(manager[INDEX_FIRST]->CloseKvStore(result[INDEX_SECOND]), OK); + result[INDEX_SECOND] = nullptr; + + /** + * @tc.steps: step4. isEncryptedDb=true, passwd=a......(129B), cipher=DEFAULT when create db. + * @tc.expected: step4. create failed and return INVALID_ARGS. + */ + password.clear(); + password.assign(PASSWD_BYTE, 'a'); + option.cipher = CipherType::DEFAULT; + option.passwd = password; + result[INDEX_THIRD] = DistributedTestTools::GetDelegateStatus(manager[INDEX_SECOND], + status, g_kvdbParameter3, option); + EXPECT_TRUE(manager[INDEX_SECOND] == nullptr && result[INDEX_THIRD] == nullptr); + EXPECT_TRUE(status == INVALID_ARGS); + + /** + * @tc.steps: step5. isEncryptedDb=false, passwd=a......(129B), cipher=DEFAULT when create db. + * @tc.expected: step5. create successfully and return OK. + */ + option.isEncryptedDb = false; + result[INDEX_FORTH] = DistributedTestTools::GetDelegateSuccess(manager[INDEX_SECOND], g_kvdbParameter3, option); + ASSERT_TRUE(manager[INDEX_SECOND] != nullptr && result[INDEX_FORTH] != nullptr); + EXPECT_EQ(manager[INDEX_SECOND]->CloseKvStore(result[INDEX_FORTH]), OK); + result[INDEX_FORTH] = nullptr; + + EXPECT_EQ(manager[INDEX_ZEROTH]->DeleteKvStore(STORE_ID_1), OK); + EXPECT_EQ(manager[INDEX_FIRST]->DeleteKvStore(STORE_ID_2), OK); + EXPECT_EQ(manager[INDEX_SECOND]->DeleteKvStore(STORE_ID_3), OK); + for (auto &item : manager) { + if (item != nullptr) { + delete item; + } + } +} + +/* + * @tc.name: OptionParam 002 + * @tc.desc: verify that isEncryptedDb and passwd are consistent with the creation time can open an existing DB. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, OptionParam002, TestSize.Level1) +{ + vector manager = {nullptr, nullptr, nullptr, nullptr}; + KvStoreDelegate *result = nullptr; + KvOption option; + + /** + * @tc.steps: step1. isEncryptedDb=true, passwd=p1, cipher=DEFAULT when create db1. + * @tc.expected: step1. create successfully and return OK. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedTestTools::GetDelegateSuccess(manager[INDEX_ZEROTH], g_kvdbParameter1, option); + ASSERT_TRUE(manager[INDEX_ZEROTH] != nullptr && result != nullptr); + EXPECT_TRUE(manager[INDEX_ZEROTH]->CloseKvStore(result) == OK); + result = nullptr; + /** + * @tc.steps: step2. isEncryptedDb=false, passwd=p1 when open db1. + * @tc.expected: step2. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + option.isEncryptedDb = false; + DBStatus status; + result = DistributedTestTools::GetDelegateStatus(manager[INDEX_FIRST], status, g_kvdbParameter1, option); + EXPECT_TRUE(manager[INDEX_FIRST] == nullptr && result == nullptr); + EXPECT_TRUE(status == INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. isEncryptedDb=true, passwd=p2 when open db1. + * @tc.expected: step3. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_2; + result = DistributedTestTools::GetDelegateStatus(manager[INDEX_FIRST], status, g_kvdbParameter1, option); + EXPECT_TRUE(manager[INDEX_FIRST] == nullptr && result == nullptr); + EXPECT_TRUE(status == INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step4. isEncryptedDb=true, passwd=p1 when open db1. + * @tc.expected: step4. open successfully and return OK. + */ + option.passwd = PASSWD_VECTOR_1; + result = DistributedTestTools::GetDelegateSuccess(manager[INDEX_FIRST], g_kvdbParameter1, option); + ASSERT_TRUE(manager[INDEX_FIRST] != nullptr && result != nullptr); + EXPECT_TRUE(manager[INDEX_FIRST]->CloseKvStore(result) == OK); + result = nullptr; + + /** + * @tc.steps: step5. isEncryptedDb=false, passwd=p1, cipher=DEFAULT when create db2. + * @tc.expected: step5. create successfully and return OK. + */ + option.isEncryptedDb = false; + result = DistributedTestTools::GetDelegateSuccess(manager[INDEX_SECOND], g_kvdbParameter2, option); + ASSERT_TRUE(manager[INDEX_SECOND] != nullptr && result != nullptr); + EXPECT_TRUE(manager[INDEX_SECOND]->CloseKvStore(result) == OK); + result = nullptr; + + /** + * @tc.steps: step6. isEncryptedDb=true, passwd=p1 when open db2. + * @tc.expected: step6. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + option.isEncryptedDb = true; + result = DistributedTestTools::GetDelegateStatus(manager[INDEX_THIRD], status, g_kvdbParameter2, option); + EXPECT_TRUE(manager[INDEX_THIRD] == nullptr && result == nullptr); + EXPECT_TRUE(status == INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step7. isEncryptedDb=false, passwd=p2 when open db2. + * @tc.expected: step7. open successfully and return OK. + */ + option.isEncryptedDb = false; + option.passwd = PASSWD_VECTOR_2; + result = DistributedTestTools::GetDelegateSuccess(manager[INDEX_THIRD], g_kvdbParameter2, option); + ASSERT_TRUE(manager[INDEX_THIRD] != nullptr && result != nullptr); + EXPECT_TRUE(manager[INDEX_THIRD]->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager[INDEX_FIRST]->DeleteKvStore(STORE_ID_1) == OK); + EXPECT_TRUE(manager[INDEX_THIRD]->DeleteKvStore(STORE_ID_2) == OK); + for (auto &item : manager) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } +} + +void ReleaseManager(KvStoreDelegateManager *&manager) +{ + if (manager != nullptr) { + delete manager; + manager = nullptr; + } + return; +} +/* + * @tc.name: RekeyDb 001 + * @tc.desc: verify that can switching a non-encrypted database to an encrypted database by Rekey. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, RekeyDb001, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result = nullptr; + KvOption option; + + /** + * @tc.steps: step1. create unencrypted db, use Rekey to update its passwd to NULL, + * then close and open without passwd. + * @tc.expected: step1. operate successfully and can open db again without passwd. + */ + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(result->Rekey(NULL_PASSWD) == OK); + DistributedTestTools::CloseAndRelease(manager, result); + option.createIfNecessary = false; + option.isEncryptedDb = false; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + ASSERT_TRUE(DistributedTestTools::Put(*result, KEY_1, VALUE_1) == DBStatus::OK); + + /** + * @tc.steps: step2. use Rekey to update db's passwd to p1=a......(100B), then close + * and open again without passwd. + * @tc.expected: step2. Rekey is ok, open db failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + vector passwordVector(VALUE_ONE_HUNDRED_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_TRUE(result->Rekey(password) == OK); + DistributedTestTools::CloseAndRelease(manager, result); + DBStatus status; + ASSERT_TRUE(DistributedTestTools::GetDelegateStatus(manager, status, g_kvdbParameter1, option) == nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. use p1 to open db and Get(k1), GetLocal(k1). + * @tc.expected: step3. open successfully and Get(k1)=v1. + */ + option.isEncryptedDb = true; + option.passwd = passwordVector; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + Value valueResult = DistributedTestTools::Get(*result, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + + /** + * @tc.steps: step4. use Rekey to update db's passwd to p2=passwordVector whose size is 128B, then close + * and open again with p1. + * @tc.expected: step4. Rekey is ok, open db failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + passwordVector.assign(BATCH_RECORDS, 'a'); + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_TRUE(result->Rekey(password) == OK); + DistributedTestTools::CloseAndRelease(manager, result); + ASSERT_TRUE(DistributedTestTools::GetDelegateStatus(manager, status, g_kvdbParameter1, option) == nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step5. use p2 to open db and delete(k1). + * @tc.expected: step5. operate successfully. + */ + option.passwd = passwordVector; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + ASSERT_TRUE(DistributedTestTools::Delete(*result, KEY_1) == OK); + + /** + * @tc.steps: step6. use Rekey to update db's passwd to p2=passwordVector whose size is 129B. + * @tc.expected: step6. Rekey failed and return INVALID_ARGS. + */ + passwordVector.assign(PASSWD_BYTE, 'a'); + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + ReleaseManager(manager); +} + +/* + * @tc.name: RekeyDb 002 + * @tc.desc: verify that can change passwd of an encrypted database by Rekey. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, RekeyDb002, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result = nullptr; + KvOption option; + + /** + * @tc.steps: step1. create encrypted db with p1=PASSWD_VECTOR_1. + * @tc.expected: step1. create successfully. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step2. use Rekey to update passwd to p1 and close db then open db again with unencrypted way. + * @tc.expected: step2. the Rekey return OK, but open db failed. + */ + EXPECT_TRUE(result->Rekey(g_passwd1) == OK); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + ReleaseManager(manager); + option.createIfNecessary = false; + option.isEncryptedDb = false; + DBStatus status; + result = DistributedTestTools::GetDelegateStatus(manager, status, g_kvdbParameter1, option); + ASSERT_TRUE(result == nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. open db with passwd=p1 and putbatch(k1,v1)(k2,v2). + * @tc.expected: step3. operate successfully. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + EXPECT_TRUE(DistributedTestTools::PutBatch(*result, entries1) == OK); + + /** + * @tc.steps: step4. use Rekey to update passwd to NULL and close db then open db with passwd=p1. + * @tc.expected: step4. the Rekey return OK, but open db failed. + */ + EXPECT_TRUE(result->Rekey(NULL_PASSWD) == OK); + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + ReleaseManager(manager); + result = DistributedTestTools::GetDelegateStatus(manager, status, g_kvdbParameter1, option); + ASSERT_TRUE(result == nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step5. open db again with unencrypted way and Get(k1), GetLocal(k2). + * @tc.expected: step5. open successfully and Get(k1)=v1, GetLocal(k2)=v2. + */ + option.isEncryptedDb = false; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + vector valueResult = DistributedTestTools::GetEntries(*result, KEY_EMPTY); + ASSERT_TRUE(valueResult.size() == entries1.size()); + + /** + * @tc.steps: step6. use Rekey to update db's passwd to p2=a......(129B). + * @tc.expected: step6. Reksy failed and return INVALID_ARGS. + */ + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + + EXPECT_TRUE(manager->CloseKvStore(result) == OK); + result = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_1) == OK); + ReleaseManager(manager); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: RekeyDb 003 + * @tc.desc: verify that do other operations during Rekey execution, the operation returns busy. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, RekeyDb003, TestSize.Level3) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result1 = nullptr; + KvStoreDelegate *result2 = nullptr; + KvOption option; + + /** + * @tc.steps: step1. use Rekey to update passwd to p1=PASSWD_VECTOR_1. + * @tc.expected: step1. create successfully. + */ + result1 = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + EXPECT_EQ(DistributedTestTools::PutBatch(*result1, entriesBatch), OK); + bool rekeyFlag1 = false; + thread subThread1([&]() { + DBStatus status = result1->Rekey(g_passwd1); + EXPECT_TRUE(status == OK || status == BUSY); + rekeyFlag1 = true; + g_conditionKvVar.notify_one(); + }); + subThread1.detach(); + + /** + * @tc.steps: step2. Call the GetKvstore interface when Rekey. + * @tc.expected: step2. the GetKvstore return BUSY. + */ + option.createIfNecessary = false; + KvStoreDelegateManager *managerRes = nullptr; + DBStatus status; + result2 = DistributedTestTools::GetDelegateStatus(managerRes, status, g_kvdbParameter1, option); + EXPECT_TRUE(status == BUSY || status == OK); + if (result2 != nullptr) { + EXPECT_EQ(managerRes->CloseKvStore(result2), OK); + ReleaseManager(managerRes); + } + std::mutex count; + { + std::unique_lock lck(count); + g_conditionKvVar.wait(lck, [&]{return rekeyFlag1;}); + } + + /** + * @tc.steps: step3. put data to db when Rekey. + * @tc.expected: step3. the put return BUSY. + */ + bool rekeyFlag2 = false; + thread subThread2([&]() { + DBStatus rekeyStatus = result1->Rekey(g_passwd2); + EXPECT_TRUE(rekeyStatus == OK || rekeyStatus == BUSY); + rekeyFlag2 = true; + g_conditionKvVar.notify_all(); + }); + subThread2.detach(); + status = DistributedTestTools::Put(*result1, KEY_1, VALUE_1); + EXPECT_TRUE(status == BUSY || status == OK); + std::unique_lock lck(count); + g_conditionKvVar.wait(lck, [&]{return rekeyFlag2;}); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} +#endif + +/* + * @tc.name: RekeyDb 004 + * @tc.desc: verify that Rekey will return busy when there are multiple instances of the same KvStore. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, RekeyDb004, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result1 = nullptr; + KvStoreDelegate *result2 = nullptr; + KvOption option; + + result1 = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + ReleaseManager(manager); + + /** + * @tc.steps: step1. use GetKvstore to open another instances of the same KvStore. + * @tc.expected: step1. open successfully. + */ + option.createIfNecessary = false; + result2 = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result2 != nullptr); + + /** + * @tc.steps: step2. call Rekey. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_EQ(result2->Rekey(g_passwd1), BUSY); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + result1 = nullptr; + EXPECT_EQ(manager->CloseKvStore(result2), OK); + result2 = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} + +void RunDbRekeyOne() +{ + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegate *result1 = nullptr; + KvOption option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter1, option); + ASSERT_TRUE(manager1 != nullptr && result1 != nullptr); + EXPECT_TRUE(result1->Rekey(g_passwd2) == OK); + EXPECT_TRUE(manager1->CloseKvStore(result1) == OK); + result1 = nullptr; + EXPECT_TRUE(manager1->DeleteKvStore(STORE_ID_1) == OK); + delete manager1; + manager1 = nullptr; +} + +void RunDbRekeyTwo() +{ + KvStoreDelegateManager *manager2 = nullptr; + KvStoreDelegate *result2 = nullptr; + KvOption option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_2; + result2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && result2 != nullptr); + EXPECT_TRUE(result2->Rekey(g_passwd1) == OK); + EXPECT_TRUE(manager2->CloseKvStore(result2) == OK); + result2 = nullptr; + EXPECT_TRUE(manager2->DeleteKvStore(STORE_ID_2) == OK); + delete manager2; + manager2 = nullptr; +} + +void RunDbRekeyThree() +{ + KvStoreDelegateManager *manager3 = nullptr; + KvStoreDelegate *result3 = nullptr; + KvOption option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result3 = DistributedTestTools::GetDelegateSuccess(manager3, g_kvdbParameter3, option); + ASSERT_TRUE(manager3 != nullptr && result3 != nullptr); + EXPECT_TRUE(result3->Rekey(NULL_PASSWD) == OK); + EXPECT_TRUE(manager3->CloseKvStore(result3) == OK); + result3 = nullptr; + EXPECT_TRUE(manager3->DeleteKvStore(STORE_ID_3) == OK); + delete manager3; + manager3 = nullptr; +} + +void RunDbRekeyFour() +{ + KvStoreDelegateManager *manager4 = nullptr; + KvStoreDelegate *result4 = nullptr; + KvOption option; + result4 = DistributedTestTools::GetDelegateSuccess(manager4, g_kvdbParameter4, option); + ASSERT_TRUE(manager4 != nullptr && result4 != nullptr); + EXPECT_TRUE(result4->Rekey(g_passwd1) == OK); + EXPECT_TRUE(manager4->CloseKvStore(result4) == OK); + result4 = nullptr; + EXPECT_TRUE(manager4->DeleteKvStore(STORE_ID_4) == OK); + delete manager4; + manager4 = nullptr; +} + +void RunDbRekeyFive() +{ + KvStoreDelegateManager *manager5 = nullptr; + KvStoreDelegate *result5 = nullptr; + KvOption option; + result5 = DistributedTestTools::GetDelegateSuccess(manager5, g_kvdbParameter5, option); + ASSERT_TRUE(manager5 != nullptr && result5 != nullptr); + vector entries1; + vector allKey1, allKey2; + GenerateRecords(BATCH_RECORDS, DEFAULT_START, allKey1, entries1, K_SEARCH_3); + DBStatus status = DistributedTestTools::PutBatch(*result5, entries1); + ASSERT_TRUE(status == DBStatus::OK); + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*result5, allKey1); + ASSERT_TRUE(statusDelete == DBStatus::OK); + status = DistributedTestTools::Put(*result5, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*result5, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + EXPECT_TRUE(manager5->CloseKvStore(result5) == OK); + result5 = nullptr; + EXPECT_TRUE(manager5->DeleteKvStore(STORE_ID_5) == OK); + delete manager5; + manager5 = nullptr; +} +/* + * @tc.name: RekeyDb 005 + * @tc.desc: verify that calling Rekey interfaces on different DBs does not affect each other.. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, RekeyDb005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create thread1 to create db1 with passwd=p1, call Rekey to update passwd to p2=PASSSWD_2. + * @tc.expected: step1. operate successfully. + */ + thread subThread1(RunDbRekeyOne); + + /** + * @tc.steps: step2. create thread2 to create db2 with passwd=p2, call Rekey to update passwd to p1=PASSSWD_1. + * @tc.expected: step2. operate successfully. + */ + thread subThread2(RunDbRekeyTwo); + + /** + * @tc.steps: step3. create thread3 to create db3 with passwd=p1, call Rekey to update passwd to NULL_PASSWD. + * @tc.expected: step3. operate successfully. + */ + thread subThread3(RunDbRekeyThree); + + /** + * @tc.steps: step4. create thread4 to create db4 without passwd, call Rekey to make db to be encrypted. + * @tc.expected: step4. operate successfully. + */ + thread subThread4(RunDbRekeyFour); + + /** + * @tc.steps: step5. create thread5 to create db5 without passwd, then CRUD data to db5. + * @tc.expected: step5. operate successfully. + */ + thread subThread5(RunDbRekeyFive); + subThread1.join(); + subThread2.join(); + subThread3.join(); + subThread5.join(); + subThread4.join(); +} + +/* + * @tc.name: SpaceManger 001 + * @tc.desc: verify that can calculate the space size normally with the existing databaseID. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, SpaceManger001, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result = nullptr; + KvOption option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + + /** + * @tc.steps: step1. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step1. call successfully and return dbSize1. + */ + uint64_t dbSize1, dbSize2, dbSize3; + dbSize1 = dbSize2 = dbSize3 = 0ul; + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize1), OK); + + /** + * @tc.steps: step2. put 100 (keys,values) to db that every item's size = 1K. + * @tc.expected: step2. operate successfully. + */ + option.createIfNecessary = false; + ReleaseManager(manager); + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + vector entriesBatch; + vector allKeys; + GenerateTenThousandRecords(NB_OPERATION_NUM, DEFAULT_START, allKeys, entriesBatch); + EXPECT_EQ(DistributedTestTools::PutBatch(*result, entriesBatch), OK); + + /** + * @tc.steps: step3. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step3. call successfully and return dbSize2, dbSize2>dbSize1. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize2), OK); + EXPECT_GT(dbSize2, dbSize1); + + /** + * @tc.steps: step4. delete the 100 (keys,values) that inserted in step2. + * @tc.expected: step4. operate successfully. + */ + EXPECT_EQ(DistributedTestTools::DeleteBatch(*result, allKeys), OK); + + /** + * @tc.steps: step5. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step5. call successfully and return dbSize3, dbSize3>dbSize2 and dbSize3 != dbSize2. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize3), OK); + EXPECT_GT(dbSize3, dbSize2); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} + +/* + * @tc.name: MergeRepeat 001 + * @tc.desc: verify that delete 9 items of (keys,v) that have the same value, can query remaining data's value is v. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvCreateTest, MergeRepeat001, TestSize.Level2) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreDelegate *result = nullptr; + result = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step1. put 10 items of (keys,v) to db and GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step1. put successfully and the GetKvStoreDiskSize() returns dbSize1. + */ + uint64_t dbSize1, dbSize2; + dbSize1 = dbSize2 = 0ul; + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize1), OK); + vector entriesBatch; + vector allKeys; + DistributedDB::Entry entry; + int putCount=0; + entry.value.assign(TWO_M_LONG_STRING, 'v'); + GenerateTenThousandRecords(OPER_CNT_END, DEFAULT_START, allKeys, entriesBatch); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_EQ(DistributedTestTools::Put(*result, iter->key, entry.value), OK); + putCount++; + } + + /** + * @tc.steps: step2. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step2. call successfully and return dbSize2, dbSize2 > dbSize1. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize2), OK); + EXPECT_TRUE(dbSize2 > dbSize1); + + /** + * @tc.steps: step3. delete the 10 items of (keys,v) except the sixth item. + * @tc.expected: step3. operate successfully. + */ + allKeys.erase(allKeys.begin() + FIVE_SECONDS); + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*result, allKeys); + EXPECT_EQ(statusDelete, DBStatus::OK); + + /** + * @tc.steps: step4. Get(k6). + * @tc.expected: step4. Get(k6)=v. + */ + Value valueResult = DistributedTestTools::Get(*result, KEY_SEARCH_6); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, entry.value)); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_crud_test.cpp new file mode 100755 index 000000000..935d5963d --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_crud_test.cpp @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvCrud { +class DistributeddbKvCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +const bool IS_LOCAL = GetRandBool(); +KvStoreDelegate *g_kvStoreDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_manager = nullptr; +void DistributeddbKvCrudTest::SetUpTestCase(void) +{ +} + +void DistributeddbKvCrudTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvCrudTest::SetUp(void) +{ + MST_LOG("SetUp TestCase before every case local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + KvOption option = g_kvOption; + option.localOnly = IS_LOCAL; + g_kvStoreDelegate = DistributedTestTools::GetDelegateSuccess(g_manager, g_kvdbParameter1, option); + ASSERT_TRUE(g_manager != nullptr && g_kvStoreDelegate != nullptr); +} + +void DistributeddbKvCrudTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after all cases."); + EXPECT_TRUE(g_manager->CloseKvStore(g_kvStoreDelegate) == OK); + g_kvStoreDelegate = nullptr; + DBStatus status = g_manager->DeleteKvStore(STORE_ID_1); + EXPECT_TRUE(status == DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_manager; + g_manager = nullptr; + RemoveDir(DIRECTOR); +} + +/* + * @tc.name: SimpleDataTest 001 + * @tc.desc: Verify that can put value to db and get successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create kv db and put (k1,v1) then get the value of k1 from db. + * @tc.expected: step1. put successfully and get the value of k1 is v1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + + /** + * @tc.steps: step2. create kv db and put (k2,v2) then get the value of k2 from db. + * @tc.expected: step2. put successfully and get the value of k2 is v2. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_2, VALUE_2); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult2 = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_2); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult2, VALUE_2)); +} + +/* + * @tc.name: SimpleDataTest 002 + * @tc.desc: Verify that can delete value and get successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create kv db and put (k1,v1) then get the value of k1 from db. + * @tc.expected: step1. put successfully and get the value of k1 is v1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + + /** + * @tc.steps: step2. delete k1 from db. + * @tc.expected: step2. delete successfully. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step3. get k1 from db. + * @tc.expected: step3. get successfully but value is null. + */ + Value valueResult2 = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult2.size() == 0); +} + +/* + * @tc.name: SimpleDataTest 003 + * @tc.desc: Verify that can put the same key with different value and get successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create kv db and put (k1,v1) then get the value of k1 from db. + * @tc.expected: step1. put successfully and get the value of k1 is v1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + + /** + * @tc.steps: step2. create kv db and put (k1,v2) then get the value of k1 from db. + * @tc.expected: step2. put successfully and get the value of k1 is v2. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_2); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + /** + * @tc.steps: step3. create kv db and put (k1,v1) then get the value of k1 from db. + * @tc.expected: step3. put successfully and get the value of k1 is v1. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); +} + +/* + * @tc.name: SimpleDataTest 004 + * @tc.desc: Verify that put a null value to db will get nothing. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest004, TestSize.Level1) +{ + DBStatus status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_EQ(status, DBStatus::OK); + + /** + * @tc.steps: step1. create kv db and put (k1,null). + * @tc.expected: step1. put ok. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_EMPTY); + ASSERT_EQ(status, DBStatus::OK); + /** + * @tc.steps: step2. get the value of k1 from db. + * @tc.expected: step2. get successfully and get the value of k1 is null. + */ + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: SimpleDataTest 005 + * @tc.desc: Verify that can not put a null key to db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create kv db and put (null,v1). + * @tc.expected: step1. put failed. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_EMPTY, VALUE_1); + ASSERT_TRUE(status == DBStatus::INVALID_ARGS); + /** + * @tc.steps: step2. get k=null from db. + * @tc.expected: step2. get value of k is null. + */ + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_EMPTY); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: SimpleDataTest 006 + * @tc.desc: Verify that can delete the entry that not exist. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest006, TestSize.Level1) +{ + /** + * @tc.steps: step1. delete k1 from db. + * @tc.expected: step1. Construct that no k1 exist in db. + */ + DBStatus status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. get k1 from db. + * @tc.expected: step2. get the value of k1 is null. + */ + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: SimpleDataTest 007 + * @tc.desc: Verify that delete absent key return OK. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest007, TestSize.Level0) +{ + /** + * @tc.steps: step1. delete k1 from db. + * @tc.expected: step1. Construct that no k1 exist in db. + */ + DBStatus status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. delete nonexistent k1 from db. + * @tc.expected: step2. delete failed but return OK. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: SimpleDataTest 008 + * @tc.desc: Verify that can the Clear interface can delete all the records one time. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest008, TestSize.Level1) +{ + /** + * @tc.steps: step1. create kv db and put (k1,v1)(k1,v2). + * @tc.expected: step1. put successfully.cd .. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_2, VALUE_2); + ASSERT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. delete all k from db then get k1 from db. + * @tc.expected: step2. delete successfully and get the value of k1 if null. + */ + status = DistributedTestTools::Clear(*g_kvStoreDelegate); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: SimpleDataTest 009 + * @tc.desc: Verify that can crud the same key continuously. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, SimpleDataTest009, TestSize.Level1) +{ + for (int cnt = 0; cnt < FIVE_TIMES; cnt++) { + /** + * @tc.steps: step1. create kv db and put (k1,v1) and check the records. + * @tc.expected: step1. put successfully and the value of the k1 in db is v1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + /** + * @tc.steps: step2. put (k1,v2) to db and get. + * @tc.expected: step2. put successfully and get the value of k1 is v2. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, KEY_1, VALUE_2); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + /** + * @tc.steps: step3. delete k1 from db and get. + * @tc.expected: step3. delete successfully and get the value of k1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + } +} + +/* + * @tc.name: ComplexDataTest 001 + * @tc.desc: Verify that can operate a long string key and key's length can not bigger than 1K. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest001, TestSize.Level0) +{ + DistributedDB::Key longKey1, longKey2; + longKey1.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + longKey2.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); // 1 Byte bigger than 1024Byte + /** + * @tc.steps: step1. create kv db and put (longKey1,null) which the length of longKey1 is 1k and get. + * @tc.expected: step1. put successfully and get the value of longKey1 is null. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, longKey1, VALUE_EMPTY); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey1); + EXPECT_TRUE(valueResult.size() == 0); + /** + * @tc.steps: step2. create kv db and put (longKey2,null) which the length of longKey2 is 1k+1 and get. + * @tc.expected: step2. put successfully and get the value of longKey2 is null. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, longKey2, VALUE_EMPTY); + ASSERT_TRUE(status != DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey2); + EXPECT_TRUE(valueResult.size() == 0); + + /** + * @tc.steps: step3. create kv db and put (longKey1,ok_v1) which the length of longKey1 is 1k and get. + * @tc.expected: step3. put successfully and get the size of longKey1.value is 1k. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, longKey1, OK_VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey1); + EXPECT_TRUE(valueResult.size() == OK_VALUE_1.size()); + /** + * @tc.steps: step4. create kv db and put (longKey2,ok_v1) which the length of longKey2 is 1k+1 and get. + * @tc.expected: step4. put failed and get the value of longKey2 is null. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, longKey2, OK_VALUE_1); + ASSERT_TRUE(status != DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey2); + EXPECT_TRUE(valueResult.size() == 0); + + /** + * @tc.steps: step5. delete longKey1 from db and get. + * @tc.expected: step5. delete successfully and get the value of longKey1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, longKey1); + ASSERT_EQ(status, DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey1); + EXPECT_TRUE(valueResult.size() == 0); + /** + * @tc.steps: step6. delete longKey2 from db and get. + * @tc.expected: step6. delete failed and get the value of longKey2 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, longKey2); + ASSERT_NE(status, DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, longKey2); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 002 + * @tc.desc: Verify that can operate a long string value and value's length can not bigger than 4M. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest002, TestSize.Level1) +{ + DistributedDB::Value longValue1, longValue2; + longValue1.assign(FOUR_M_LONG_STRING, (uint8_t)'a'); + longValue2.assign(FOUR_M_LONG_STRING + 1, (uint8_t)'b'); + + /** + * @tc.steps: step1. create kv db and put (OK_K1,longValue1) which the length of longValue1 is 4M and get. + * @tc.expected: step1. put successfully and get the value of OK_K1 is longValue1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, longValue1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == FOUR_M_LONG_STRING); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, longValue1)); + + /** + * @tc.steps: step2. create kv db and put (OK_K1,longValue2) which the length of longValue2 is 4M+1 and get. + * @tc.expected: step2. put failed and get the size of longValue2 is 4M. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, longValue2); + ASSERT_TRUE(status != DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == FOUR_M_LONG_STRING); + + /** + * @tc.steps: step3. delete OK_KEY_1 and get. + * @tc.expected: step3. delete successfully and get the value of OK_KEY_1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, OK_KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 003 + * @tc.desc: Verify that can operate a alphanum string key. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest003, TestSize.Level1) +{ + DistributedDB::Key alphanumKey1; + char chr; + for (chr = 'a'; chr <= 'z'; ++chr) { + alphanumKey1.push_back(chr); + } + for (chr = 'A'; chr <= 'Z'; ++chr) { + alphanumKey1.push_back(chr); + } + for (chr = '0'; chr <= '9'; ++chr) { + alphanumKey1.push_back(chr); + } + + /** + * @tc.steps: step1. create kv db and put (alphanumKey1,null) that alphanumKey1=[a-zA-Z0-9]. + * @tc.expected: step1. put successfully and get the value of alphanumKey1 is null. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, alphanumKey1, VALUE_EMPTY); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, alphanumKey1); + EXPECT_TRUE(valueResult.size() == 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_EMPTY)); + + /** + * @tc.steps: step2. create kv db and put (alphanumKey1,OK_VALUE_1) that alphanumKey1=[a-zA-Z0-9]. + * @tc.expected: step2. put successfully and get the value of alphanumKey1 is OK_VALUE_1. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, alphanumKey1, OK_VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, alphanumKey1); + EXPECT_TRUE(valueResult.size() == OK_VALUE_1.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, OK_VALUE_1)); + + /** + * @tc.steps: step3. delete alphanumKey1 from db and get. + * @tc.expected: step3. delete successfully and get the value of alphanumKey1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, alphanumKey1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, alphanumKey1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 004 + * @tc.desc: Verify that can operate a alphanum string value. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest004, TestSize.Level1) +{ + DistributedDB::Value alphanumValue1, alphanumValue2; + int chr; + for (chr = 'a'; chr <= 'z'; ++chr) { + alphanumValue1.push_back(chr); + alphanumValue2.push_back('a' + 'z' - chr); + } + for (chr = 'A'; chr <= 'Z'; ++chr) { + alphanumValue1.push_back(chr); + alphanumValue2.push_back('A' + 'Z' - chr); + } + for (chr = '0'; chr <= '9'; ++chr) { + alphanumValue1.push_back(chr); + alphanumValue2.push_back('0' + '9' - chr); + } + + /** + * @tc.steps: step1. create kv db and put (OK_KEY_1,alphanumValue1) that alphanumValue1=[a-zA-Z0-9]. + * @tc.expected: step1. put successfully and get the value of OK_KEY_1 is alphanumValue1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, alphanumValue1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == alphanumValue1.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, alphanumValue1)); + + /** + * @tc.steps: step2. create kv db and put (OK_KEY_1,alphanumValue2) that alphanumValue1=[z-aZ-A9-0]. + * @tc.expected: step2. put successfully and get the value of OK_KEY_1 is alphanumValue2. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, alphanumValue2); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == alphanumValue2.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, alphanumValue2)); + + /** + * @tc.steps: step3. delete OK_KEY_1 from db and get. + * @tc.expected: step3. delete successfully and get the value of OK_KEY_1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, OK_KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 005 + * @tc.desc: Verify that can operate a full ASCII set string key. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest005, TestSize.Level1) +{ + DistributedDB::Key fullAsciiKey1; + for (int cVal = 0; cVal <= 255; ++cVal) { + fullAsciiKey1.push_back(cVal); + } + + /** + * @tc.steps: step1. create kv db and put (fullAsciiKey1,null) that fullAsciiKey1=[\0-\255]. + * @tc.expected: step1. put successfully and get the value of fullAsciiKey1 is null. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, fullAsciiKey1, VALUE_EMPTY); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, fullAsciiKey1); + EXPECT_TRUE(valueResult.size() == 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_EMPTY)); + + /** + * @tc.steps: step2. create kv db and put (fullAsciiKey1,OK_VALUE_1) that fullAsciiKey1=[\0-\255]. + * @tc.expected: step2. put successfully and get the value of fullAsciiKey1 is OK_VALUE_1. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, fullAsciiKey1, OK_VALUE_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, fullAsciiKey1); + EXPECT_TRUE(valueResult.size() == OK_VALUE_1.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, OK_VALUE_1)); + + /** + * @tc.steps: step3. delete fullAsciiKey1 from db and get. + * @tc.expected: step3. delete successfully and get the value of fullAsciiKey1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, fullAsciiKey1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, fullAsciiKey1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 006 + * @tc.desc: Verify that can operate a full ASCII set string value. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest006, TestSize.Level1) +{ + DistributedDB::Value fullAsciiValue1, fullAsciiValue2; + for (int cVal = 0; cVal <= 255; ++cVal) { + fullAsciiValue1.push_back(cVal); + fullAsciiValue2.push_back(255 - cVal); + } + + /** + * @tc.steps: step1. create kv db and put (OK_KEY_1,fullAsciiValue1) that fullAsciiValue1=[\0-\255]. + * @tc.expected: step1. put successfully and get the value of OK_KEY_1 is fullAsciiValue1. + */ + DBStatus status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, fullAsciiValue1); + ASSERT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == fullAsciiValue1.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, fullAsciiValue1)); + + /** + * @tc.steps: step2. create kv db and put (OK_KEY_1,fullAsciiValue2) that fullAsciiValue2=[\255-\0]. + * @tc.expected: step2. put successfully and get the value of OK_KEY_1 is fullAsciiValue2. + */ + status = DistributedTestTools::Put(*g_kvStoreDelegate, OK_KEY_1, fullAsciiValue2); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == fullAsciiValue2.size()); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, fullAsciiValue2)); + + /** + * @tc.steps: step3. delete OK_KEY_1 from db and get. + * @tc.expected: step3. delete successfully and get the value of OK_KEY_1 is null. + */ + status = DistributedTestTools::Delete(*g_kvStoreDelegate, OK_KEY_1); + ASSERT_TRUE(status == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_kvStoreDelegate, OK_KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: ComplexDataTest 007 + * @tc.desc: Verify that can operate chinese string key and value. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ComplexDataTest007, TestSize.Level1) +{ + /* + * @tc.steps: step1. create kv db and put(k,OK_VALUE_1) that k="中文"and get. + * @tc.expected: step1. put successfully and get the value of k is OK_VALUE_1. + */ +} + +/* + * @tc.name: ReadWritePerformance 001 + * @tc.desc: calculate the time of put-get in 16b-100b/16b-100kb, 1k times' random-put&get. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvCrudTest, ReadWritePerformance001, TestSize.Level3) +{ + const int PERFORMANCE_SIZE = 6; + PerformanceData performanceData[PERFORMANCE_SIZE] = { + /** + * @tc.steps: step1. put/get 16B key,100B value with random model. + * @tc.expected: step1. Performance of put&get is normal. + */ + { 1, 16, 100, false, false, false, false, IS_LOCAL }, + /** + * @tc.steps: step2. put/get 16B key,100KB value with random model. + * @tc.expected: step2. Performance of put&get is normal. + */ + { 1, 16, 100 * 1000, false, false, false, false, IS_LOCAL }, + /** + * @tc.steps: step3. put/get 16B key,100B value with random model. + * @tc.expected: step3. Performance of put&get is normal. + */ + { 50, 16, 100, false, false, false, false, IS_LOCAL }, + /** + * @tc.steps: step4. put/get 16B key,100KB value with random model. + * @tc.expected: step4. Performance of put&get is normal. + */ + { 50, 16, 100 * 1000, false, false, false, false, IS_LOCAL }, + /** + * @tc.steps: step5. put/get 16B key,100B value with random model. + * @tc.expected: step5. Performance of put&get is normal. + */ + { 100, 16, 100, false, false, false, false, IS_LOCAL }, + /** + * @tc.steps: step6. put/get 16B key,100B value with random model. + * @tc.expected: step6. Performance of put&get is normal. + */ + { 100, 16, 100 * 1000, false, false, false, false, IS_LOCAL } + }; + + for (int ps = 0; ps < PERFORMANCE_SIZE; ++ps) { + DistributedTestTools::CalculateOpenPerformance(performanceData[ps]); + DistributedTestTools::CalculateInsertPerformance(performanceData[ps]); + DistributedTestTools::CalculateGetPutPerformance(performanceData[ps]); + DistributedTestTools::CalculateUpdatePerformance(performanceData[ps]); + DistributedTestTools::CalculateGetUpdatePerformance(performanceData[ps]); + DistributedTestTools::CalculateUseClearPerformance(performanceData[ps]); + } +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_snap_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_snap_test.cpp new file mode 100755 index 000000000..ec49c5beb --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_snap_test.cpp @@ -0,0 +1,1581 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributedDBKvObserverSnap { +const bool IS_LOCAL = false; +const int CHANGED_ZERO_TIME = 0; +const int CHANGED_ONE_TIME = 1; +const int OPER_CNT_START = 0; +const int OPER_CNT_END = 5; +const int TEN_RECORDS = 10; +const int DELETE_CNT_START = 0; +const int DELETE_CNT_END = 2; + +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +DistributedDB::CipherPassword g_filePasswd1; +DistributedDB::CipherPassword g_filePasswd2; + +class DistributeddbKvObserverSnapTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +KvStoreDelegate *g_observerSnapDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_observerSnapManager = nullptr; +void DistributeddbKvObserverSnapTest::SetUpTestCase(void) +{ + MST_LOG("SetUpTestCase before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + g_observerSnapDelegate = DistributedTestTools::GetDelegateSuccess(g_observerSnapManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_observerSnapManager != nullptr && g_observerSnapDelegate != nullptr); + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + (void)g_filePasswd1.SetValue(FILE_PASSWD_VECTOR_1.data(), FILE_PASSWD_VECTOR_1.size()); + (void)g_filePasswd2.SetValue(FILE_PASSWD_VECTOR_2.data(), FILE_PASSWD_VECTOR_2.size()); +} + +void DistributeddbKvObserverSnapTest::TearDownTestCase(void) +{ + EXPECT_EQ(g_observerSnapManager->CloseKvStore(g_observerSnapDelegate), OK); + g_observerSnapDelegate = nullptr; + DBStatus status = g_observerSnapManager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_observerSnapManager; + g_observerSnapManager = nullptr; + RemoveDir(DIRECTOR); +} + +void DistributeddbKvObserverSnapTest::SetUp(void) +{ + ASSERT_TRUE(g_observerSnapDelegate != nullptr); + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbKvObserverSnapTest::TearDown(void) +{ +} + +/* + * @tc.name: Register 001 + * @tc.desc: Verify that can register a fresh observer base on kv db snap. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register001, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. RegisterSnapObserver base on kv db snap. + * @tc.expected: step1. Register successfully. + */ + KvStoreSnapshotDelegate *snap = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_NE(snap, nullptr); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snap); + snap = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Register 002 + * @tc.desc: Verify that can register a null observer on kv db snap. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register002, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. Register a null observer base on kv db snap. + * @tc.expected: step1. Register successfully. + */ + KvStoreSnapshotDelegate *snap = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, nullptr); + EXPECT_NE(snap, nullptr); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snap); + snap = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Register 003 + * @tc.desc: Verify that can register only once with the same observer. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register003, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer 6 times base on kv db snap. + * @tc.expected: step1. First register is successful and 5 times later return null. + */ + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + KvStoreSnapshotDelegate *snap = nullptr; + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + snap = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_EQ(snap, nullptr); + } + + /** + * @tc.steps: step2. Register Put (k1,v1) to db. + * @tc.expected: step2. First put successfully and the observer return inserting data once. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertList; + insertList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertList)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Register 004 + * @tc.desc: Verify that can register different observers. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register004, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer1, observer2; + + /** + * @tc.steps: step1. Register observer1 bases on kv db snap. + * @tc.expected: step1. Register successfully. + */ + KvStoreSnapshotDelegate *snapShot1 = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer1); + EXPECT_NE(snapShot1, nullptr); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and the observer1 return 1 inserting data info. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries1; + insertEntries1.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observer1.Clear(); + + /** + * @tc.steps: step3. register a null observer bases on kv db snap. + * @tc.expected: step3. register successfully. + */ + KvStoreSnapshotDelegate *snapShotNull = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, nullptr); + EXPECT_NE(snapShotNull, nullptr); + + /** + * @tc.steps: step4. put (k2,v2) to db. + * @tc.expected: step4. register successfully. + */ + status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_2, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries1.clear(); + insertEntries1.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observer1.Clear(); + + /** + * @tc.steps: step5. register observer2 bases on kv db snap. + * @tc.expected: step5. register successfully. + */ + KvStoreSnapshotDelegate *snapShot2 = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer2); + EXPECT_NE(snapShot2, nullptr); + + /** + * @tc.steps: step6. put (k3,v3) to db. + * @tc.expected: step6. put successfully and the observer1,2 return 1 inserting data info individually. + */ + status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_3, VALUE_3); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries1.clear(); + insertEntries1.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observer1.Clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observer2.Clear(); + + EXPECT_EQ((g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot1)), DBStatus::OK); + snapShot1 = nullptr; + EXPECT_EQ((g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShotNull)), DBStatus::OK); + snapShotNull = nullptr; + EXPECT_EQ((g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot2)), DBStatus::OK); + snapShot2 = nullptr; +} + +/* + * @tc.name: Register 005 + * @tc.desc: Verify that unregister observer (release snapshot) won't return capture data info. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register005, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer1 bases on kv db snap. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and the observer1 return 1 inserting data info. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + insertEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + /** + * @tc.steps: step3. unregister observer1 (release snapshot). + * @tc.expected: step3. operate successfully. + */ + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + EXPECT_TRUE(statusRelease == DBStatus::OK); + + /** + * @tc.steps: step4. put (k2,v2) to db. + * @tc.expected: step4. put successfully and has nothing changed data info returned. + */ + status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_2, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); +} + +/* + * @tc.name: Register 006 + * @tc.desc: Verify that register observer, and then unregister it many times will success only firstly. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register006, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer1 bases on kv db snap. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + + /** + * @tc.steps: step2. unregister observer1 for 5 times. + * @tc.expected: step2. Firstly register successfully and another failed. + */ + DBStatus statusRelease; + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + if (time == OPER_CNT_START) { + EXPECT_TRUE(statusRelease == DBStatus::OK); + } + else { + EXPECT_TRUE(statusRelease != DBStatus::OK); + } + } + + /** + * @tc.steps: step3. put (k1,v1) to db. + * @tc.expected: step3. put successfully and has nothing changed data info returned. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); +} + +/* + * @tc.name: Register 007 + * @tc.desc: Verify that the DB was not register a snapshot can't release it. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register007, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(delegate != nullptr) << "fail to create exist kvdb"; + + /** + * @tc.steps: step1. Register observer1 bases on kv db1 snap and unregister it bases on kv db2. + * @tc.expected: step1. Register successfully and unregister failed. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + DBStatus statusRelease = delegate->ReleaseKvStoreSnapshot(snapShot); + EXPECT_TRUE(statusRelease != DBStatus::OK); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + DBStatus status = manager->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager; + manager = nullptr; + statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: Register 008 + * @tc.desc: Verify that observer will not be triggered after released. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register008, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + KvOption option = g_kvOption; + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter2, option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + option.createIfNecessary = IS_NOT_NEED_CREATE; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + + /** + * @tc.steps: step1. Register observer1 bases on kv db2 snap and close db then put(k1,v1). + * @tc.expected: step1. Register successfully and close failed, observer1 return a changed data info. + */ + KvStoreObserverImpl observer1; + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(delegate1, &observer1); + EXPECT_NE(snapShot, nullptr); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) != OK); + DBStatus status = DistributedTestTools::Put(*delegate2, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertList; + insertList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertList)); + observer1.Clear(); + + /** + * @tc.steps: step2. unregister observer1 and close db then put. + * @tc.expected: step2. Unregister and close successfully, observer1 return nothing. + */ + EXPECT_TRUE(delegate1->ReleaseKvStoreSnapshot(snapShot) == DBStatus::OK); + snapShot = nullptr; + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + status = DistributedTestTools::Put(*delegate2, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + insertList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, insertList)); + observer1.Clear(); + + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + status = manager2->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: Register 009 + * @tc.desc: Delete kv db, and verify observer will not be activated. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Register009, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + + /** + * @tc.steps: step1. Register observer1 bases on kv db2 snap and close db then put(k1,v1). + * @tc.expected: step1. Register successfully and close failed, observer1 return a changed data info. + */ + KvStoreObserverImpl observer1; + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(delegate1, &observer1); + EXPECT_NE(snapShot, nullptr); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) != OK); + + DBStatus status = DistributedTestTools::Put(*delegate1, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + + /** + * @tc.steps: step2. Unregister observer1 and close db then delete db. + * @tc.expected: step2. operate successfully and observer1 return nothing. + */ + EXPECT_TRUE(delegate1->ReleaseKvStoreSnapshot(snapShot) == DBStatus::OK); + snapShot = nullptr; + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + EXPECT_TRUE(manager1->DeleteKvStore(STORE_ID_2) == OK); + + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager1; + manager1 = nullptr; +} + +/* + * @tc.name: DataChange 001 + * @tc.desc: verify that can observer for inserting a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange001, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. Register observer1 bases on kv db2 snap. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapShot != nullptr); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and observer1 return a insert data info. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + insertEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + status = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapShot); + snapShot = nullptr; + EXPECT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: DataChange 002 + * @tc.desc: verify that can observer for updating a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange002, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2 snap. + * @tc.expected: step1. operate successfully to construct data and observer exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. update (k1,v1) to (k1,v2). + * @tc.expected: step2. update successfully and observer return a updating data info. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + vector updateEntries; + updateEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + observer.Clear(); + + status = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: DataChange 003 + * @tc.desc: verify that can observer for deleting a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange003, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2 snap. + * @tc.expected: step1. operate successfully to construct data and observer exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. delete from kb. + * @tc.expected: step2. delete successfully and observer return a deleting data info. + */ + DBStatus status = DistributedTestTools::Delete(*g_observerSnapDelegate, KEY_1); + EXPECT_TRUE(status == DBStatus::OK); + vector deleteEntries; + deleteEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 004 + * @tc.desc: verify that can observer for batch inserting. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange004, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 snap and putBatch (k1,v1)(k2,v2). + * @tc.expected: step1. operate successfully. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return a batch inserting data info. + */ + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entries1)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 005 + * @tc.desc: verify that can observer for batch updating. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange005, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2 snap. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. updateBatch (k1,v1)(k2,v2) to (k1,v2)(k2,v3) and check callback. + * @tc.expected: step2. observer1 return a batch updating data info. + */ + vector entries2; + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_2_3); + statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries2); + EXPECT_TRUE(statusPut == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entries2)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 006 + * @tc.desc: verify that can observer for batch deleting. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange006, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2 snap. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. deleteBatch (k1)(k2) from db and check callback. + * @tc.expected: step2. observer1 return a batch deleting data info. + */ + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*g_observerSnapDelegate, KEYS_1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entries1)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 007 + * @tc.desc: verify that can observer for clearing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange007, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2 snap. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. deleteBatch (k1)(k2) from db and check callback. + * @tc.expected: step2. observer1 return a clear data info. + */ + DBStatus statusClear = DistributedTestTools::Clear(*g_observerSnapDelegate); + EXPECT_TRUE(statusClear == DBStatus::OK); + + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 008 + * @tc.desc: verify that observer won't be activated if inserting null key. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange008, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 snap and put (k=null,v=ok). + * @tc.expected: step1. register successfully and put failed. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + DBStatus statusPut = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_EMPTY, OK_VALUE_1); + EXPECT_TRUE(statusPut != DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return nothing. + */ + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: DataChange 009 + * @tc.desc: verify that observer won't be activated if batch inserting failed. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange009, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 snap and putBatch (k1=null,v1)(k2,v2). + * @tc.expected: step1. register successfully and putBatch failed. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + vector entries; + entries.push_back(ENTRY_NULL_1); // null key with VALUE_1. + entries.push_back(ENTRY_2); + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entries); + EXPECT_TRUE(statusPut != DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return nothing. + */ + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +void ObserverSnapVerifyInsert(KvStoreDelegate *&delegate1, + vector &observers) +{ + vector insertEntries1; + vector insertEntries2; + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_A_1, VALUE_A_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + insertEntries1.push_back(ENTRY_A_1); + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observers[0].Clear(); + + insertEntries2.push_back(ENTRY_A_1); + EXPECT_TRUE(VerifyObserverResult(observers[1], CHANGED_ONE_TIME, INSERT_LIST, insertEntries2)); + observers[1].Clear(); +} + +vector ObserverSnapVerifyInsertBatch(KvStoreDelegate *&delegate1, vector &observers) +{ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observers[0].Clear(); + EXPECT_TRUE(VerifyObserverResult(observers[1], CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observers[1].Clear(); + return entriesBatch; +} + +void ObserverSnapVerifyDelete(KvStoreDelegate *&delegate1, vector &observers, + DistributedDB::Entry &entry) +{ + vector deleteEntries; + DBStatus statusDelete = DistributedTestTools::Delete(*delegate1, KEY_A_1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + deleteEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observers[0].Clear(); + + vector deleteEntries2; + deleteEntries2.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observers[1], CHANGED_ONE_TIME, DELETE_LIST, deleteEntries2)); + observers[1].Clear(); +} + +void ObserverSnapVerifyUpdate(KvStoreDelegate *&delegate1, KvStoreDelegate *&delegate2, + vector &observers, vector &entriesBatch, + KvStoreSnapshotDelegate *&snapshot2) +{ + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut == DBStatus::OK); + entriesBatch.front().value = VALUE_2; + + vector updateEntries1; + updateEntries1.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, UPDATE_LIST, updateEntries1)); + observers[0].Clear(); + + vector updateEntries2; + updateEntries2.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observers[1], CHANGED_ONE_TIME, UPDATE_LIST, updateEntries2)); + observers[1].Clear(); + + DBStatus statusRelease = delegate2->ReleaseKvStoreSnapshot(snapshot2); + snapshot2 = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); + + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observers[0].Clear(); +} + +void ObserverSnapVerifyDeleteBatch(KvStoreDelegate *&delegate1, vector &observers, + vector &entriesBatch) +{ + vector deleteEntries1; + vector keys1; + vector deleteBatch; + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + keys1.push_back(entriesBatch.back().key); + deleteBatch.push_back(entriesBatch.back()); + entriesBatch.pop_back(); + } + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*delegate1, keys1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + deleteEntries1.clear(); + deleteEntries1 = deleteBatch; + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, DELETE_LIST, deleteEntries1)); + observers[0].Clear(); + + DBStatus statusClear = DistributedTestTools::Clear(*delegate1); + EXPECT_TRUE(statusClear == DBStatus::OK); + + deleteEntries1.clear(); + EXPECT_TRUE(VerifyObserverResult(observers[0], CHANGED_ONE_TIME, DELETE_LIST, deleteEntries1)); + observers[0].Clear(); +} + +/* + * @tc.name: DataChange 010 + * @tc.desc: verify that can observer for complex data changing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange010, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, + g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr) << "fail to create exist kvdb"; + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, + g_kvdbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + KvStoreObserverImpl observer1, observer2; + vector observers; + observers.push_back(observer1); + observers.push_back(observer2); + + /** + * @tc.steps: step1. register observer1 bases on kv db snap1. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot1 = DistributedTestTools::RegisterSnapObserver(delegate1, &observers[0]); + EXPECT_TRUE(snapshot1 != nullptr); + + /** + * @tc.steps: step2. register observer2 bases on kv db snap2. + * @tc.expected: step2. register successfully. + */ + KvStoreSnapshotDelegate *snapshot2 = DistributedTestTools::RegisterSnapObserver(delegate2, &observers[1]); + EXPECT_TRUE(snapshot2 != nullptr); + + /** + * @tc.steps: step3. put (k="abc",v=a1") to db. + * @tc.expected: step3. put successfully and both of observer1,2 return a putting data info. + */ + ObserverSnapVerifyInsert(delegate1, observers); + + /** + * @tc.steps: step3. putBatch 10 items of (keys,values) to db. + * @tc.expected: step3. putBatch successfully and both of observer1,2 return a batch putting data info. + */ + vector entriesBatch = ObserverSnapVerifyInsertBatch(delegate1, observers); + + /** + * @tc.steps: step4. delete (k="abc") from db. + * @tc.expected: step4. delete successfully and both of observer1,2 return a deleting data info. + */ + DistributedDB::Entry entry = ENTRY_A_1; + ObserverSnapVerifyDelete(delegate1, observers, entry); + + /** + * @tc.steps: step5. update (k1,v1) to (k1,v2). + * @tc.expected: step5. update successfully and both of observer1,2 return a updating data info. + */ + ObserverSnapVerifyUpdate(delegate1, delegate2, observers, entriesBatch, snapshot2); + + /** + * @tc.steps: step6. deleteBatch (keys1) from db. + * @tc.expected: step6. deleteBatch successfully and both of observer1,2 return a batch deleting data info. + */ + ObserverSnapVerifyDeleteBatch(delegate1, observers, entriesBatch); + + /** + * @tc.steps: step7. unregister observer1. + * @tc.expected: step7. unregister successfully. + */ + DBStatus statusRelease = delegate1->ReleaseKvStoreSnapshot(snapshot1); + snapshot1 = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + DBStatus status = manager2->DeleteKvStore(STORE_ID_2); + EXPECT_EQ(status, DistributedDB::DBStatus::OK); + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; +} + +vector ObserverSnapVerifyTransactionCommit(KvStoreDelegate *&delegate1, KvStoreObserverImpl &observer1) +{ + vector entryBatches; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entryBatches); + list deleteBatch; + DBStatus statusStart = delegate1->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entryBatches); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusDelete = DistributedTestTools::Delete(*delegate1, KEY_1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + deleteBatch.push_front(entryBatches[0]); + entryBatches.erase(entryBatches.begin()); + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_2, VALUE_3); + EXPECT_TRUE(statusPut == DBStatus::OK); + entryBatches.front().value = VALUE_3; + vector keys1; + for (int time = DELETE_CNT_START; time < static_cast(DELETE_CNT_END); ++time) { + keys1.push_back(entryBatches.back().key); + deleteBatch.push_front(entryBatches.back()); + entryBatches.pop_back(); + } + statusDelete = DistributedTestTools::DeleteBatch(*delegate1, keys1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + vector insertEntries, updateEntries, deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries)); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + DBStatus statusCommit = delegate1->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + insertEntries.clear(); + for (auto eachEntry : entryBatches) { + insertEntries.push_back(eachEntry); + } + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + updateEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + deleteEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + return entryBatches; +} + +void ObserverSnapVerifyTransactionRollback(KvStoreDelegate *&delegate2, KvStoreObserverImpl &observer2) +{ + vector entriesBatch; + vector allKeys2; + GenerateRecords(TEN_RECORDS, DEFAULT_ANOTHER_START, allKeys2, entriesBatch); + + DBStatus statusStart = delegate2->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate2, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusDelete = DistributedTestTools::Delete(*delegate2, entriesBatch[0].key); + EXPECT_TRUE(statusDelete == DBStatus::OK); + DBStatus statusPut = DistributedTestTools::Put(*delegate2, entriesBatch[1].key, VALUE_3); + EXPECT_TRUE(statusPut == DBStatus::OK); + + vector keys2; + for (int time = DELETE_CNT_START; time < static_cast(DELETE_CNT_END); ++time) { + keys2.push_back(entriesBatch.at(time).key); + } + statusDelete = DistributedTestTools::DeleteBatch(*delegate2, keys2); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + vector updateEntries; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries)); + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + DBStatus statusRollback = delegate2->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + + // step4: check the result. + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + updateEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries)); + deleteEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + observer2.Clear(); +} + +/* + * @tc.name: DataChange 011 + * @tc.desc: verify that can observer for transaction operating changing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, DataChange011, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer1; + KvStoreObserverImpl observer2; + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegateManager *transactionManager1 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(transactionManager1, + g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(transactionManager1 != nullptr && delegate1 != nullptr); + + /** + * @tc.steps: step1. register observer1 bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot1 = DistributedTestTools::RegisterSnapObserver(delegate1, &observer1); + EXPECT_TRUE(snapshot1 != nullptr); + + /** + * @tc.steps: step2. start transaction, putBatch 10(keys,values), delete(k1), put(k2,v3), deleteBatch(k9)(k8). + * @tc.expected: step2. operate successfully and observer1 return corresponding changed data info. + */ + vector entriesBatch = ObserverSnapVerifyTransactionCommit(delegate1, observer1); + + /** + * @tc.steps: step3. unregister observer1. + * @tc.expected: step3. unregister successfully. + */ + DBStatus statusRelease = delegate1->ReleaseKvStoreSnapshot(snapshot1); + snapshot1 = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); + EXPECT_TRUE(transactionManager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *transactionManager2 = nullptr; + delegate2 = DistributedTestTools::GetDelegateSuccess(transactionManager2, + g_kvdbParameter1_2_2, g_kvOption); + ASSERT_TRUE(transactionManager2 != nullptr && delegate2 != nullptr); + + /** + * @tc.steps: step4. register observer2 bases on kv db snap. + * @tc.expected: step4. register successfully. + */ + KvStoreSnapshotDelegate *snapshot2 = DistributedTestTools::RegisterSnapObserver(delegate2, &observer2); + EXPECT_TRUE(snapshot2 != nullptr); + + /** + * @tc.steps: step5. repeat step2 but don'commit transaction ,rollback it. + * @tc.expected: step5. operate successfully and observer2 return nothing. + */ + ObserverSnapVerifyTransactionRollback(delegate2, observer2); + + statusRelease = delegate2->ReleaseKvStoreSnapshot(snapshot2); + snapshot2 = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); + EXPECT_TRUE(transactionManager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + DBStatus status = transactionManager2->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DistributedDB::DBStatus::OK); + delete transactionManager1; + transactionManager1 = nullptr; + delete transactionManager2; + transactionManager2 = nullptr; +} + +/* + * @tc.name: Performance 001 + * @tc.desc: test performance of subscribing for inserting, deleting, updating one record. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance001, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + KvStoreObserverImpl observer; + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + + /** + * @tc.steps: step2. put (k1,v1) to db and caclute the time of observer return info. + * @tc.expected: step2. put successfully, printf the time of observer return info after insert data. + */ + DBStatus status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + insertEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + double duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to insert a record costs: %lldus.", (long long int)duration); + + /** + * @tc.steps: step3. put (k1,v2) to db and caclute the time of observer return info. + * @tc.expected: step3. put successfully, printf the time of observer return info after update data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + status = DistributedTestTools::Put(*g_observerSnapDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + vector updateEntries; + updateEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to update a record costs: %lldus.", (long long int)duration); + + /** + * @tc.steps: step4. delete (k1) from db and caclute the time of observer return info. + * @tc.expected: step4. delete successfully, printf the time of observer return info after delete data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + status = DistributedTestTools::Delete(*g_observerSnapDelegate, KEY_1); + EXPECT_TRUE(status == DBStatus::OK); + vector deleteEntries; + deleteEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to delete a record costs: %lldus.", (long long int)duration); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +void ObserverSnapBatchPerformance(KvStoreObserverImpl &observer, vector &entriesBatch) +{ + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observer.Clear(); + + double duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to update 10 records costs: %lldus.", (long long int)duration); + observer.Clear(); +} + +/* + * @tc.name: Performance 002 + * @tc.desc: test performance of subscribing for batch inserting, deleting, updating. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance002, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. putBatch 10 items (keys,values) to db and caclute the time of observer return info. + * @tc.expected: step2. putBatch successfully,printf the time of observer return info after insert 10 data. + */ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer.Clear(); + double duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to insert 10 records costs: %lldus.", (long long int)duration); + + /** + * @tc.steps: step3. updateBatch (keys,values) and caclute the time of observer return info. + * @tc.expected: step3. updateBatch successfully,printf the time of observer return info after updateBatch data. + */ + observer.Clear(); + ObserverSnapBatchPerformance(observer, entriesBatch); + + /** + * @tc.steps: step4. deleteBatch (keys) from db and caclute the time of observer return info. + * @tc.expected: step4. deleteBatch successfully,printf the time of observer return info after deleteBatch data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus status = DistributedTestTools::DeleteBatch(*g_observerSnapDelegate, allKeys); + EXPECT_TRUE(status == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entriesBatch)); + observer.Clear(); + + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing to delete 10 records costs: %lldus.", (long long int)duration); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +void ObserverSnapTransactionPerformance(vector &entriesBatch, + vector &allKeys, KvStoreObserverImpl &observer) +{ + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusStart = g_observerSnapDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusCommit = g_observerSnapDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observer.Clear(); + + double duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing a transaction to update 10 records costs: %lldus.", + (long long int)duration); + + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + statusStart = g_observerSnapDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus status = DistributedTestTools::DeleteBatch(*g_observerSnapDelegate, allKeys); + EXPECT_TRUE(status == DBStatus::OK); + statusCommit = g_observerSnapDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entriesBatch)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG("Getting notice from subscribing to delete a record costs: %lldus.", (long long int)duration); +} + +/* + * @tc.name: Performance 003 + * @tc.desc: test performance of subscribing for transaction operation. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance003, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. start transaction and putBatch 10 items (keys,values) to db then commit. + * @tc.expected: step2. operate successfully. + */ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusStart = g_observerSnapDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerSnapDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusCommit = g_observerSnapDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + /** + * @tc.steps: step3. check collback and calculate the duration of step2. + * @tc.expected: step3. observer return batch inserting data info. + */ + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer.Clear(); + + double duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick).count(); + MST_LOG(" Getting notice from subscribing a transaction to insert 10 records costs: %lldus.", + (long long int)duration); + + ObserverSnapTransactionPerformance(entriesBatch, allKeys, observer); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Performance 004 + * @tc.desc: test system info of subscribing for inserting, deleting, updating one record. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance004, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + vector insertEntries; + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Performance 005 + * @tc.desc: test system info of subscribing for batch inserting, deleting, updating. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance005, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + vector insertEntries; + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: Performance 006 + * @tc.desc: test system info of subscribing for transaction operation. + * @tc.type: Performance + * @tc.require: SR000BUH3K,SR000BVRNE + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverSnapTest, Performance006, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerSnapDelegate); + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(g_observerSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + vector insertEntries; + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = g_observerSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); +} + +/* + * @tc.name: RekeyDb 001 + * @tc.desc: verify that Rekey will return busy when there are registered snapshot. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvObserverSnapTest, RekeyDb001, TestSize.Level0) +{ + KvStoreDelegate *kvObserverSnapDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + KvOption option; + + kvObserverSnapDelegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter2, option); + ASSERT_TRUE(manager != nullptr && kvObserverSnapDelegate != nullptr); + + /** + * @tc.steps: step1. register an observer bases on kv db snap. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapshot = DistributedTestTools::RegisterSnapObserver(kvObserverSnapDelegate, &observer); + EXPECT_TRUE(snapshot != nullptr); + + /** + * @tc.steps: step2. call Rekey to update passwd to passwd_1. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_TRUE(kvObserverSnapDelegate->Rekey(g_passwd1) == BUSY); + + DBStatus statusRelease = kvObserverSnapDelegate->ReleaseKvStoreSnapshot(snapshot); + snapshot = nullptr; + EXPECT_EQ(statusRelease, DBStatus::OK); + + EXPECT_TRUE(manager->CloseKvStore(kvObserverSnapDelegate) == OK); + kvObserverSnapDelegate = nullptr; + + DBStatus status = manager->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager; + manager = nullptr; +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_test.cpp new file mode 100755 index 000000000..852d252e9 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_observer_test.cpp @@ -0,0 +1,1684 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributedDBKvObserver { +const bool IS_LOCAL = false; +const long CHANGED_ZERO_TIME = 0; +const long CHANGED_ONE_TIME = 1; +const unsigned long OPER_CNT_START = 0; +const unsigned long OPER_CNT_END = 5; +const unsigned long TEN_RECORDS = 10; +const unsigned long DELETE_CNT_START = 0; +const unsigned long DELETE_CNT_END = 2; + +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +DistributedDB::CipherPassword g_filePasswd1; +DistributedDB::CipherPassword g_filePasswd2; + +class DistributeddbKvObserverTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +KvStoreDelegate *g_observerDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_observerManager = nullptr; +void DistributeddbKvObserverTest::SetUpTestCase(void) +{ + MST_LOG("SetUpTestCase before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + g_observerDelegate = DistributedTestTools::GetDelegateSuccess(g_observerManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_observerManager != nullptr && g_observerDelegate != nullptr); + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + (void)g_filePasswd1.SetValue(FILE_PASSWD_VECTOR_1.data(), FILE_PASSWD_VECTOR_1.size()); + (void)g_filePasswd2.SetValue(FILE_PASSWD_VECTOR_2.data(), FILE_PASSWD_VECTOR_2.size()); +} + +void DistributeddbKvObserverTest::TearDownTestCase(void) +{ + EXPECT_TRUE(g_observerManager->CloseKvStore(g_observerDelegate) == OK); + g_observerDelegate = nullptr; + DBStatus status; + status = g_observerManager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_observerManager; + g_observerManager = nullptr; + RemoveDir(DIRECTOR); +} + +void DistributeddbKvObserverTest::SetUp(void) +{ + EXPECT_TRUE(g_observerDelegate != nullptr); + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbKvObserverTest::TearDown(void) +{ +} + +/* + * @tc.name: ObserverRegister 001 + * @tc.desc: Verify that can register a fresh observer base on kv db. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister001, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer base on kv db. + * @tc.expected: step1. Register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverRegister 002 + * @tc.desc: Verify that can register a null observer on kv db. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister002, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. Register a null observer base on kv db. + * @tc.expected: step1. Register failed. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, nullptr); + EXPECT_TRUE(status != DBStatus::OK); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, nullptr); + EXPECT_TRUE(statusRelease != DBStatus::OK); +} + +/* + * @tc.name: ObserverRegister 003 + * @tc.desc: Verify that can register only once with the same observer. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister003, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer 6 times base on kv db. + * @tc.expected: step1. First register is successful and 5 times later return null. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status != DBStatus::OK); + } + + /** + * @tc.steps: step2. Register Put (k1,v1) to db. + * @tc.expected: step2. First put successfully and the observer return inserting data once. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverRegister 004 + * @tc.desc: Verify that can register different observers. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister004, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer1, observer2; + /** + * @tc.steps: step1. Register observer1 bases on kv db. + * @tc.expected: step1. Register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer1); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and the observer1 return 1 inserting data info. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + + /** + * @tc.steps: step3. register a null observer bases on kv db. + * @tc.expected: step3. register successfully. + */ + status = DistributedTestTools::RegisterObserver(g_observerDelegate, nullptr); + EXPECT_TRUE(status != DBStatus::OK); + /** + * @tc.steps: step4. put (k2,v2) to db. + * @tc.expected: step4. register successfully. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_2, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries.clear(); + DistributedDB::Entry entry2 = { KEY_2, VALUE_2 }; + insertEntries.push_back(entry2); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + + /** + * @tc.steps: step5. register observer2 bases on kv db. + * @tc.expected: step5. register successfully. + */ + status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer2); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step6. put (k3,v3) to db. + * @tc.expected: step6. put successfully and the observer1,2 return 1 inserting data info individually. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_3, VALUE_3); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries.clear(); + DistributedDB::Entry entry3 = { KEY_3, VALUE_3 }; + insertEntries.push_back(entry3); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer2.Clear(); + // step5: clear the preset of step1(repair the config and stub) + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer1); + EXPECT_TRUE(statusRelease == DBStatus::OK); + statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer2); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverRegister 005 + * @tc.desc: Verify that unregister observer (release snapshot) won't return changed data info. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister005, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer1 bases on kv db. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and the observer1 return 1 inserting data info. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + /** + * @tc.steps: step3. unregister observer1 (release snapshot). + * @tc.expected: step3. operate successfully. + */ + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); + + /** + * @tc.steps: step4. put (k2,v2) to db. + * @tc.expected: step4. put successfully and has nothing changed data info returned. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_2, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + insertEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); +} + +/* + * @tc.name: ObserverRegister 006 + * @tc.desc: Verify that register observer, and then unregister it many times will success only firstly. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister006, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. Register observer1 bases on kv db. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. unregister observer1 for 5 times. + * @tc.expected: step2. Firstly register successfully and another failed. + */ + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + if (time == OPER_CNT_START) { + EXPECT_TRUE(statusRelease == DBStatus::OK); + } + else { + EXPECT_TRUE(statusRelease != DBStatus::OK); + } + } + + /** + * @tc.steps: step3. put (k1,v1) to db. + * @tc.expected: step3. put successfully and has nothing changed data info returned. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); +} + +/* + * @tc.name: ObserverRegister 007 + * @tc.desc: Verify that unregister an unregistered observer will fail. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister007, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(manager, + g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr) << "fail to create exist kvdb"; + + /** + * @tc.steps: step1. Register observer1 bases on kv db1 snap and unregister it bases on kv db2. + * @tc.expected: step1. Register successfully and unregister failed. + */ + KvStoreObserverImpl observer; + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(delegate, &observer); + + EXPECT_TRUE(statusRelease != DBStatus::OK); + + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + DBStatus status = manager->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ObserverRegister 008 + * @tc.desc: Verify that observer will not be called after unregister. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister008, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + + /** + * @tc.steps: step1. open delegate1 and delegate2 from hv db2. + * @tc.expected: step1. open successfully. + */ + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + EXPECT_TRUE(delegate1 != nullptr) << "fail to create exist kvdb"; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2_1_1, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + + /** + * @tc.steps: step2. Register observer1 bases on kv db2 and close delegate1. + * @tc.expected: step2. Register and close successfully. + */ + KvStoreObserverImpl observer1; + DBStatus status = DistributedTestTools::RegisterObserver(delegate1, &observer1); + EXPECT_TRUE(status == DBStatus::OK); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + + /** + * @tc.steps: step3. put (k1,v1) to delegate2. + * @tc.expected: step3. observer1 return nothing. + */ + status = DistributedTestTools::Put(*delegate2, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + status = manager2->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DBStatus::OK) << "fail to delete exist kvdb"; + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: ObserverRegister 009 + * @tc.desc: Delete kv db, and verify observer will not be activated. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRegister009, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, + g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr) << "fail to create exist kvdb"; + + /** + * @tc.steps: step1. Register observer1 bases on kv db2 and put(k1,v1). + * @tc.expected: step1. Register successfully and observer1 return a inserting data info. + */ + KvStoreObserverImpl observer1; + DBStatus status = DistributedTestTools::RegisterObserver(delegate1, &observer1); + EXPECT_TRUE(status == OK); + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_1, VALUE_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer1.Clear(); + + /** + * @tc.steps: step2. close db then delete db. + * @tc.expected: step2. operate successfully and observer1 return nothing. + */ + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + EXPECT_TRUE(manager1->DeleteKvStore(STORE_ID_2) == OK); + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + + delete manager1; + manager1 = nullptr; +} + +/* + * @tc.name: DataChange 001 + * @tc.desc: verify that can observer for inserting a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange001, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. Register observer1 bases on kv db2. + * @tc.expected: step1. Register successfully to construct observer exist. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully and observer1 return a insert data info. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 002 + * @tc.desc: verify that can observer for updating a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange002, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2. + * @tc.expected: step1. operate successfully to construct data and observer exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. update (k1,v1) to (k1,v2). + * @tc.expected: step2. update successfully and observer return a updating data info. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + + vector updateEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_2 }; + updateEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 003 + * @tc.desc: verify that can observer for deleting a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange003, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2. + * @tc.expected: step1. operate successfully to construct data and observer exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. delete from kb. + * @tc.expected: step2. delete successfully and observer return a deleting data info. + */ + status = DistributedTestTools::Delete(*g_observerDelegate, KEY_1); + EXPECT_TRUE(status == DBStatus::OK); + + vector deleteEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + deleteEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 004 + * @tc.desc: verify that can observer for batch inserting. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange004, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 and putBatch (k1,v1)(k2,v2). + * @tc.expected: step1. operate successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return a batch inserting data info. + */ + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entries1)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 005 + * @tc.desc: verify that can observer for batch updating. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange005, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. updateBatch (k1,v1)(k2,v2) to (k1,v2)(k2,v3) and check callback. + * @tc.expected: step2. observer1 return a batch updating data info. + */ + vector entries2; + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_2_3); + statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries2); + EXPECT_TRUE(statusPut == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entries2)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 006 + * @tc.desc: verify that can observer for batch deleting. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange006, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. deleteBatch (k1)(k2) from db and check callback. + * @tc.expected: step2. observer1 return a batch deleting data info. + */ + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*g_observerDelegate, keys1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entries1)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 007 + * @tc.desc: verify that can observer for clearing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange007, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) and register observer1 bases on kv db2. + * @tc.expected: step1. operate successfully to construct data and observer1 exist. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. deleteBatch (k1)(k2) from db and check callback. + * @tc.expected: step2. observer1 return a clear data info. + */ + DBStatus statusClear = DistributedTestTools::Clear(*g_observerDelegate); + EXPECT_TRUE(statusClear == DBStatus::OK); + + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 008 + * @tc.desc: verify that observer won't be activated if inserting null key. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange008, TestSize.Level0) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 and put (k=null,v=ok). + * @tc.expected: step1. register successfully and put failed. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + DBStatus statusPut = DistributedTestTools::Put(*g_observerDelegate, KEY_EMPTY, OK_VALUE_1); + EXPECT_TRUE(statusPut != DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return nothing. + */ + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: DataChange 009 + * @tc.desc: verify that observer won't be activated if batch inserting failed. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange009, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register observer1 bases on kv db2 and putBatch (k1=null,v1)(k2,v2). + * @tc.expected: step1. register successfully and putBatch failed. + */ + KvStoreObserverImpl observer; + + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + vector entries; + entries.push_back(ENTRY_NULL_1); // null key with VALUE_1. + entries.push_back(ENTRY_2); + DBStatus statusPut = DistributedTestTools::PutBatch(*g_observerDelegate, entries); + EXPECT_TRUE(statusPut != DBStatus::OK); + + /** + * @tc.steps: step2. check callback. + * @tc.expected: step2. observer1 return nothing. + */ + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +vector KvObserverVerifyInsert(KvStoreDelegate *&delegate1, KvStoreDelegate *&delegate2, + KvStoreObserverImpl &observer1, KvStoreObserverImpl &observer2) +{ + vector insertEntries1; + vector insertEntries2; + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_A_1, VALUE_A_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + DistributedDB::Entry entry = { KEY_A_1, VALUE_A_1 }; + insertEntries1.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, insertEntries1)); + observer1.Clear(); + insertEntries2.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, insertEntries2)); + observer2.Clear(); + + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer1.Clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer2.Clear(); + + DBStatus statusDelete = DistributedTestTools::Delete(*delegate1, KEY_A_1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + vector deleteEntries1; + deleteEntries1.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries1)); + observer1.Clear(); + vector deleteEntries2; + deleteEntries2.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries2)); + observer2.Clear(); + + return entriesBatch; +} + +vector KvObserverVerifyUpdateAndDelete(KvStoreDelegate *&delegate1, KvStoreDelegate *&delegate2, + KvStoreObserverImpl &observer1, KvStoreObserverImpl &observer2, vector &entriesBatch) +{ + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut == DBStatus::OK); + entriesBatch.front().value = VALUE_2; + vector updateEntries1; + DistributedDB::Entry entry1 = { KEY_1, VALUE_2 }; + updateEntries1.push_back(entry1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries1)); + observer1.Clear(); + vector updateEntries2; + updateEntries2.push_back(entry1); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries2)); + observer2.Clear(); + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(delegate2, &observer2); + EXPECT_TRUE(statusRelease == DBStatus::OK); + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observer1.Clear(); + updateEntries2.clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries2)); + observer2.Clear(); + vector keys1; + vector deleteBatch; + for (int time = OPER_CNT_START; time < static_cast(OPER_CNT_END); ++time) { + keys1.push_back(entriesBatch.back().key); + deleteBatch.push_back(entriesBatch.back()); + entriesBatch.pop_back(); + } + DBStatus statusDelete = DistributedTestTools::DeleteBatch(*delegate1, keys1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + vector deleteEntries1; + deleteEntries1.clear(); + deleteEntries1 = deleteBatch; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries1)); + return deleteEntries1; +} + +/* + * @tc.name: DataChange 010 + * @tc.desc: verify that can observer for complex data changing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange010, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(manager1, + g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr) << "fail to create exist kvdb"; + KvOption option = g_kvOption; + option.createIfNecessary = IS_NOT_NEED_CREATE; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + KvStoreObserverImpl observer1, observer2; + + /** + * @tc.steps: step1. register observer1 bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(delegate1, &observer1); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. register observer2 bases on kv db. + * @tc.expected: step2. register successfully. + */ + status = DistributedTestTools::RegisterObserver(delegate2, &observer2); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step3. put (k="abc",v="a1") and putBatch 10 items (keys,values) then delete (k=abc). + * @tc.expected: step3. operate successfully and both of observer1,2 return changes data info. + */ + vector entriesBatch = KvObserverVerifyInsert(delegate1, delegate2, observer1, observer2); + + /** + * @tc.steps: step4. update (k1,v1) to (k1,v2), release observer2 and putBatch then deleteBatch. + * @tc.expected: step4. operate successfully and only observer1 return changed data info. + */ + vector deleteEntries1 = KvObserverVerifyUpdateAndDelete(delegate1, delegate2, + observer1, observer2, entriesBatch); + observer1.Clear(); + + /** + * @tc.steps: step5. clear db. + * @tc.expected: step5. observer1 return a delete data info. + */ + DBStatus statusClear = DistributedTestTools::Clear(*delegate1); + EXPECT_TRUE(statusClear == DBStatus::OK); + deleteEntries1.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries1)); + observer1.Clear(); + + /** + * @tc.steps: step6. unregister observer1. + * @tc.expected: step6. unregister successfully. + */ + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(delegate1, &observer1); + EXPECT_TRUE(statusRelease == DBStatus::OK); + EXPECT_TRUE(manager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + status = manager2->DeleteKvStore(STORE_ID_2); + EXPECT_EQ(status, DistributedDB::DBStatus::OK); + delete manager1; + manager1 = nullptr; + delete manager2; + manager2 = nullptr; +} + +void KvObserverVerifyTransactionCommit(KvStoreObserverImpl &observer1, + KvStoreDelegate *&delegate1, KvStoreDelegateManager *&transactionManager1) +{ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + DBStatus statusStart = delegate1->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate1, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusDelete = DistributedTestTools::Delete(*delegate1, KEY_1); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + entriesBatch.erase(entriesBatch.begin()); + DBStatus statusPut = DistributedTestTools::Put(*delegate1, KEY_2, VALUE_3); + EXPECT_TRUE(statusPut == DBStatus::OK); + entriesBatch.front().value = VALUE_3; + vector keys; + for (int time = DELETE_CNT_START; time < static_cast(DELETE_CNT_END); ++time) { + keys.push_back(entriesBatch.back().key); + entriesBatch.pop_back(); + } + statusDelete = DistributedTestTools::DeleteBatch(*delegate1, keys); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + vector updateEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries)); + vector deleteEntries; + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + DBStatus statusCommit = delegate1->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + updateEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + deleteEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer1.Clear(); + EXPECT_TRUE(DistributedTestTools::UnRegisterObserver(delegate1, &observer1) == OK); + EXPECT_TRUE(transactionManager1->CloseKvStore(delegate1) == OK); + delegate1 = nullptr; +} + +void KvObserverVerifyTransactionRollback(KvStoreObserverImpl &observer2, KvStoreDelegate *&delegate2) +{ + vector entriesBatch2; + vector allKeys2; + GenerateRecords(TEN_RECORDS, DEFAULT_ANOTHER_START, allKeys2, entriesBatch2); + + DBStatus statusStart = delegate2->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate2, entriesBatch2); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusDelete = DistributedTestTools::Delete(*delegate2, entriesBatch2[0].key); + EXPECT_TRUE(statusDelete == DBStatus::OK); + entriesBatch2.erase(entriesBatch2.begin()); + DBStatus statusPut = DistributedTestTools::Put(*delegate2, entriesBatch2[1].key, VALUE_3); + EXPECT_TRUE(statusPut == DBStatus::OK); + entriesBatch2.front().value = VALUE_3; + + vector keys2; + for (int time = DELETE_CNT_START; time < static_cast(DELETE_CNT_END); ++time) { + keys2.push_back(entriesBatch2.at(time).key); + } + statusDelete = DistributedTestTools::DeleteBatch(*delegate2, keys2); + EXPECT_TRUE(statusDelete == DBStatus::OK); + + DBStatus statusRollback = delegate2->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + + // step4: check the result. + vector insertEntries2; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries2)); + observer2.Clear(); + vector updateEntries2; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, UPDATE_LIST, updateEntries2)); + observer2.Clear(); + vector deleteEntries2; + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, DELETE_LIST, deleteEntries2)); + observer2.Clear(); +} + +/* + * @tc.name: DataChange 011 + * @tc.desc: verify that can observer for transaction operating changing. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, DataChange011, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer1; + KvStoreObserverImpl observer2; + + KvStoreDelegate *delegate1 = nullptr; + KvStoreDelegateManager *transactionManager1 = nullptr; + delegate1 = DistributedTestTools::GetDelegateSuccess(transactionManager1, + g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(transactionManager1 != nullptr && delegate1 != nullptr); + + /** + * @tc.steps: step1. register observer1 bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(delegate1, &observer1); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. start transaction, putBatch 10(keys,values), delete(k1), put(k2,v3), deleteBatch(k9)(k8). + * @tc.expected: step2. operate successfully and observer1 return corresponding changed data info. + */ + KvObserverVerifyTransactionCommit(observer1, delegate1, transactionManager1); + + /** + * @tc.steps: step3. register observer2 bases on kv db. + * @tc.expected: step3. register successfully. + */ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *transactionManager2 = nullptr; + delegate2 = DistributedTestTools::GetDelegateSuccess(transactionManager2, g_kvdbParameter1_2_2, g_kvOption); + ASSERT_TRUE(transactionManager2 != nullptr && delegate2 != nullptr); + status = DistributedTestTools::RegisterObserver(delegate2, &observer2); + EXPECT_TRUE(delegate2 != nullptr); + + /** + * @tc.steps: step4. repeat step2 but don'commit transaction ,rollback it. + * @tc.expected: step4. operate successfully and observer2 return nothing. + */ + KvObserverVerifyTransactionRollback(observer2, delegate2); + + EXPECT_TRUE(DistributedTestTools::UnRegisterObserver(delegate2, &observer2) == OK); + EXPECT_TRUE(transactionManager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + + status = transactionManager2->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(status == DistributedDB::DBStatus::OK); + delete transactionManager1; + transactionManager1 = nullptr; + delete transactionManager2; + transactionManager2 = nullptr; +} + +/* + * @tc.name: ObserverPressure 001 + * @tc.desc: verify that support many observers for inserting a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPressure001, TestSize.Level2) +{ + int countPerDelegate = 8; + + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register 8 observers bases on kv db. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observers[OBSERVER_NUM]; + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *transactionManagers = nullptr; + int obsCnt; + + for (obsCnt = OBSERVER_CNT_START; obsCnt < static_cast(OBSERVER_CNT_END); ++obsCnt) { + if (obsCnt % countPerDelegate == 0) { + delegate = DistributedTestTools::GetDelegateSuccess(transactionManagers, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(transactionManagers != nullptr && delegate != nullptr); + } + DBStatus status = DistributedTestTools::RegisterObserver(delegate, &observers[obsCnt]); + EXPECT_TRUE(status == OK); + } + + /** + * @tc.steps: step2. put (k1,v="ok"). + * @tc.expected: step2. put successfully and all of observers can return a inserting data info. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_observerDelegate, KEY_1, OK_VALUE_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, OK_VALUE_1 }; + insertEntries.push_back(entry); + for (obsCnt = OBSERVER_CNT_START; obsCnt < static_cast(OBSERVER_CNT_END); ++obsCnt) { + EXPECT_TRUE(VerifyObserverResult(observers[obsCnt], CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observers[obsCnt].Clear(); + } + + for (obsCnt = OBSERVER_CNT_START; obsCnt < static_cast(OBSERVER_CNT_END); ++obsCnt) { + EXPECT_EQ(DistributedTestTools::UnRegisterObserver(delegate, &observers[obsCnt]), OK); + if (obsCnt % countPerDelegate == 7) { + EXPECT_TRUE(transactionManagers->CloseKvStore(delegate) == OK); + delegate = nullptr; + delete transactionManagers; + transactionManagers = nullptr; + } + } +} + +/* + * @tc.name: ObserverPressure 002 + * @tc.desc: verify that support observer for inserting a big key record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPressure002, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. put (k,v) that the size of k is 1K. + * @tc.expected: step2. put successfully and observer can return a inserting data info. + */ + Entry entryCurrent; + entryCurrent.key.assign(ONE_K_LONG_STRING, 'a'); + entryCurrent.value = VALUE_1; + status = DistributedTestTools::Put(*g_observerDelegate, entryCurrent.key, entryCurrent.value); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + insertEntries.push_back(entryCurrent); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverPressure 003 + * @tc.desc: verify that support observer for inserting a big value record. + * @tc.type: FUNC + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPressure003, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. put (k,v) that the size of v is 4M. + * @tc.expected: step2. put successfully and observer can return a inserting data info. + */ + Entry entryCurrent; + entryCurrent.key = KEY_1; + entryCurrent.value.assign(FOUR_M_LONG_STRING, 'a'); + status = DistributedTestTools::Put(*g_observerDelegate, entryCurrent.key, entryCurrent.value); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + insertEntries.push_back(entryCurrent); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverPerformance 001 + * @tc.desc: test performance of subscribing for inserting, deleting, updating one record. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance001, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + + /** + * @tc.steps: step2. put (k1,v1) to db and caclute the time of observer return info. + * @tc.expected: step2. put successfully, printf the time of observer return info after insert data. + */ + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + vector insertEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + insertEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + auto duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to insert a record costs: %lldus.", (long long)duration.count()); + observer.Clear(); + + /** + * @tc.steps: step3. put (k1,v2) to db and caclute the time of observer return info. + * @tc.expected: step3. put successfully, printf the time of observer return info after update data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + status = DistributedTestTools::Put(*g_observerDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(status == DBStatus::OK); + vector updateEntries; + entry.value = VALUE_2; + updateEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to update a record costs: %lldus.", (long long)duration.count()); + observer.Clear(); + + /** + * @tc.steps: step4. delete (k1) from db and caclute the time of observer return info. + * @tc.expected: step4. delete successfully, printf the time of observer return info after delete data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + status = DistributedTestTools::Delete(*g_observerDelegate, KEY_1); + EXPECT_TRUE(status == DBStatus::OK); + vector deleteEntries; + deleteEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to delete a record costs: %lldus.", (long long)duration.count()); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +void KvObserverBatchPerformance(KvStoreObserverImpl &observer, vector &entriesBatch) +{ + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observer.Clear(); + + auto duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to update 10 records costs: %lldus.", (long long)duration.count()); +} + +/* + * @tc.name: ObserverPerformance 002 + * @tc.desc: test performance of subscribing for batch inserting, deleting, updating. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance002, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. putBatch 10 items (keys,values) to db and caclute the time of observer return info. + * @tc.expected: step2. putBatch successfully,printf the time of observer return info after insert 10 data. + */ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer.Clear(); + + auto duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to insert 10 records costs: %lldus.", (long long)duration.count()); + + /** + * @tc.steps: step3. updateBatch (keys,values) and caclute the time of observer return info. + * @tc.expected: step3. updateBatch successfully,printf the time of observer return info after updateBatch data. + */ + KvObserverBatchPerformance(observer, entriesBatch); + + /** + * @tc.steps: step4. deleteBatch (keys) from db and caclute the time of observer return info. + * @tc.expected: step4. deleteBatch successfully,printf the time of observer return info after deleteBatch data. + */ + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + status = DistributedTestTools::DeleteBatch(*g_observerDelegate, allKeys); + EXPECT_TRUE(status == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entriesBatch)); + observer.Clear(); + + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to delete 10 records costs: %lldus.", (long long)duration.count()); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +void KvObserverTransactionPerformance(KvStoreObserverImpl &observer, vector &entriesBatch) +{ + for (auto itEntriesBatch = entriesBatch.begin(); itEntriesBatch != entriesBatch.end(); ++itEntriesBatch) { + itEntriesBatch->value.push_back('A'); + } + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusStart = g_observerDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusCommit = g_observerDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, UPDATE_LIST, entriesBatch)); + observer.Clear(); + + auto duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing a transaction to update 10 records costs: %lldus.", + (long long)duration.count()); +} + +/* + * @tc.name: ObserverPerformance 003 + * @tc.desc: test performance of subscribing for transaction operation. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance003, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. start transaction and putBatch 10 items (keys,values) to db then commit. + * @tc.expected: step2. operate successfully. + */ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + auto tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + DBStatus statusStart = g_observerDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*g_observerDelegate, entriesBatch); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + DBStatus statusCommit = g_observerDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + /** + * @tc.steps: step3. check collback and calculate the duration of step2. + * @tc.expected: step3. observer return batch inserting data info. + */ + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, INSERT_LIST, entriesBatch)); + observer.Clear(); + auto duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing a transaction to insert 10 records costs: %lldus.", + (long long)duration.count()); + + /** + * @tc.steps: step4. start transaction and deleteBatch 10 items (keys,values) to db then commit. + * @tc.expected: step4. operate successfully. + */ + KvObserverTransactionPerformance(observer, entriesBatch); + tick = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + statusStart = g_observerDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + status = DistributedTestTools::DeleteBatch(*g_observerDelegate, allKeys); + EXPECT_TRUE(status == DBStatus::OK); + statusCommit = g_observerDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + /** + * @tc.steps: step5. check collback and calculate the duration of step4. + * @tc.expected: step5. observer return batch deleting data info. + */ + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ONE_TIME, DELETE_LIST, entriesBatch)); + observer.Clear(); + duration = std::chrono::duration_cast(observer.GetOnChangeTime() - tick); + MST_LOG(" Getting notice from subscribing to delete a record costs: %lldus.", (long long)duration.count()); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverPerformance 004 + * @tc.desc: test system info of subscribing for inserting, deleting, updating one record. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance004, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + KvStoreObserverImpl observer; + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverPerformance 005 + * @tc.desc: test system info of subscribing for batch inserting, deleting, updating. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance005, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +/* + * @tc.name: ObserverPerformance 006 + * @tc.desc: test system info of subscribing for transaction operation. + * @tc.type: Performance + * @tc.require: SR000BUH3K + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverPerformance006, TestSize.Level3) +{ + DistributedTestTools::Clear(*g_observerDelegate); + + /** + * @tc.steps: step1. register an observer bases on kv db. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + vector insertEntries; + EXPECT_TRUE(VerifyObserverResult(observer, CHANGED_ZERO_TIME, INSERT_LIST, insertEntries)); + observer.Clear(); + + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(g_observerDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ObserverRekeyDb 001 + * @tc.desc: verify that Rekey will return busy when there are registered observer or putting data to db. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvObserverTest, ObserverRekeyDb001, TestSize.Level3) +{ + KvStoreDelegate *kvObserverDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + KvOption option; + + kvObserverDelegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter2, option); + ASSERT_TRUE(manager != nullptr && kvObserverDelegate != nullptr); + + /** + * @tc.steps: step1. register observer. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observer; + DBStatus status = DistributedTestTools::RegisterObserver(kvObserverDelegate, &observer); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. call Rekey to update passwd to passwd_1. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_TRUE(kvObserverDelegate->Rekey(g_passwd1) == BUSY); + + /** + * @tc.steps: step3. unregister observer. + * @tc.expected: step3. unregister successfully. + */ + DBStatus statusRelease = DistributedTestTools::UnRegisterObserver(kvObserverDelegate, &observer); + EXPECT_TRUE(statusRelease == DBStatus::OK); + + /** + * @tc.steps: step4. put (1k,4M) of (k,v) to db. + * @tc.expected: step4. put successfully. + */ + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, BATCH_RECORDS, ONE_K_LONG_STRING, FOUR_M_LONG_STRING); + thread subThread([&]() { + DBStatus rekeyStatus = DistributedTestTools::PutBatch(*kvObserverDelegate, entriesBatch); + EXPECT_TRUE(rekeyStatus == OK || rekeyStatus == BUSY); + }); + subThread.detach(); + + /** + * @tc.steps: step5. call Rekey to update passwd to passwd_2. + * @tc.expected: step5. Rekey returns BUSY. + */ + status = kvObserverDelegate->Rekey(g_passwd2); + EXPECT_TRUE(status == OK || status == BUSY); + std::this_thread::sleep_for(std::chrono::seconds(NB_OPERATION_CNT_END)); + EXPECT_TRUE(manager->CloseKvStore(kvObserverDelegate) == OK); + kvObserverDelegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_2) == OK); + delete manager; + manager = nullptr; +} +#endif +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_realdel_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_realdel_test.cpp new file mode 100755 index 000000000..eef868000 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_realdel_test.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvRealdel { +static std::condition_variable g_kvBackupVar; +class DistributeddbKvRealdelTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +KvStoreDelegate *g_kvBackupDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_manager = nullptr; +void DistributeddbKvRealdelTest::SetUpTestCase(void) +{ +} + +void DistributeddbKvRealdelTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvRealdelTest::SetUp(void) +{ + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + KvOption option = g_kvOption; + g_kvBackupDelegate = DistributedTestTools::GetDelegateSuccess(g_manager, g_kvdbParameter1, option); + ASSERT_TRUE(g_manager != nullptr && g_kvBackupDelegate != nullptr); +} + +void DistributeddbKvRealdelTest::TearDown(void) +{ + MST_LOG("TearDown after case."); + EXPECT_TRUE(g_manager->CloseKvStore(g_kvBackupDelegate) == OK); + g_kvBackupDelegate = nullptr; + DBStatus status = g_manager->DeleteKvStore(STORE_ID_1); + EXPECT_TRUE(status == DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_manager; + g_manager = nullptr; +} + +/* + * @tc.name: KvDeleteAll 001 + * @tc.desc: test that delete interface will real delete data. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll001, TestSize.Level1) +{ + /** + * @tc.steps: step1. put (k1, v1), (k2, v2) to db, and update (k1, v1) to (k1, v2). + * @tc.expected: step1. put successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step2. use sqlite to open the db and check the data of k1. + * @tc.expected: step2. there is only one data of k1 in db. + */ + std::string identifier = g_kvdbParameter1.userId + "-" + g_kvdbParameter1.appId + "-" + g_kvdbParameter1.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string kvDbName = DIRECTOR + hashIdentifierRes + KVMULTIDB; + std::vector keyS1, keyS2; + keyS1.push_back(KEY_1); + int count = 0; + EXPECT_TRUE(DistributedTestTools::RepeatCheckAsyncResult([&kvDbName, &count, &keyS1]()->bool { + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), QUERY_SQL, keyS1, g_kvOption, count); + return count == ONE_RECORD; + }, 5, 500)); // query 5 times every 500 ms. + /** + * @tc.steps: step3. delete k1 and then check data of k1 and k2; + * @tc.expected: step3. can't find k1, but can find the value of k2 is v2. + */ + EXPECT_EQ(g_kvBackupDelegate->Delete(KEY_1), OK); + Value realValue; + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_1); + EXPECT_TRUE(realValue.size() == 0); + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_2); + EXPECT_TRUE(realValue == VALUE_2); + /** + * @tc.steps: step4. wait for some seconds and check the data of k1; + * @tc.expected: step4. can't find k1. + */ + EXPECT_TRUE(DistributedTestTools::RepeatCheckAsyncResult([&kvDbName, &count, &keyS1]()->bool { + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), QUERY_SQL, keyS1, g_kvOption, count); + return count == 0; + }, 5, 500)); // query 5 times every 500 ms. +} + +/* + * @tc.name: KvDeleteAll 002 + * @tc.desc: test that deleteBatch interface will real delete data. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll002, TestSize.Level1) +{ + /** + * @tc.steps: step1. put (k1, v1), (k2, v2), (k3, v3) to db and use deletebatch interface to delete k1, k2. + * @tc.expected: step1. put and delete successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_3, VALUE_3), OK); + std::vector keyS; + keyS.push_back(KEY_1); + keyS.push_back(KEY_2); + EXPECT_EQ(g_kvBackupDelegate->DeleteBatch(keyS), OK); + /** + * @tc.steps: step2. open the db and check the data of k1, k2, k3. + * @tc.expected: step2. there is only k3 in db and can't get k1, k2. + */ + Value realValue; + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_1); + EXPECT_TRUE(realValue.size() == 0); + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_2); + EXPECT_TRUE(realValue.size() == 0); + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_3); + EXPECT_TRUE(realValue == VALUE_3); + /** + * @tc.steps: step3. wait for some seconds and check the data of k1, k2 by sqlite; + * @tc.expected: step3. can't find k1, k2. + */ + std::string identifier = g_kvdbParameter1.userId + "-" + g_kvdbParameter1.appId + "-" + g_kvdbParameter1.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string kvDbName = DIRECTOR + hashIdentifierRes + KVMULTIDB; + int count = 0; + EXPECT_TRUE(DistributedTestTools::RepeatCheckAsyncResult([&kvDbName, &count, &keyS]()->bool { + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), MULTI_KEY_QUERY_SQL, keyS, g_kvOption, count); + return count == 0; + }, 5, 500)); // query 5 times every 500 ms. +} + +/* + * @tc.name: KvDeleteAll 003 + * @tc.desc: test that clear interface will real delete data. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll003, TestSize.Level1) +{ + /** + * @tc.steps: step1. put (k1, v1), (k2, v2) to db and update (k1, v1) to (k1, v2). + * @tc.expected: step1. put and update successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_2, VALUE_2), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step2. clear the db, and check the data of k1, k2. + * @tc.expected: step2. clear and can't find k1, k2. + */ + EXPECT_EQ(g_kvBackupDelegate->Clear(), OK); + Value realValue; + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_1); + EXPECT_TRUE(realValue.size() == 0); + realValue = DistributedTestTools::Get(*g_kvBackupDelegate, KEY_2); + EXPECT_TRUE(realValue.size() == 0); + + /** + * @tc.steps: step3. wait for 5 seconds and check the data of k1, k2; + * @tc.expected: step3. can't find k1, k2. + */ + std::string identifier = g_kvdbParameter1.userId + "-" + g_kvdbParameter1.appId + "-" + g_kvdbParameter1.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string kvDbName = DIRECTOR + hashIdentifierRes + KVMULTIDB; + std::vector keyS; + keyS.push_back(KEY_1); + keyS.push_back(KEY_2); + int count = 0; + EXPECT_TRUE(DistributedTestTools::RepeatCheckAsyncResult([&kvDbName, &count, &keyS]()->bool { + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), MULTI_KEY_QUERY_SQL, keyS, g_kvOption, count); + return count == 0; + }, 5, 500)); // query 5 times every 500 ms. + + EncrypteAttribute attribute = {g_kvOption.isEncryptedDb, g_kvOption.passwd}; + EXPECT_TRUE(DistributedTestTools::RepeatCheckAsyncResult([&kvDbName, &attribute, &count]()->bool { + DistributedTestTools::QuerySpecifiedData(kvDbName, SYNC_MULTI_VER_QUERY_SQL, attribute, count); + return count == ONE_RECORD; + }, 5, 500)); // query 5 times every 500 ms. +} + +/* + * @tc.name: KvDeleteAll 004 + * @tc.desc: test that it can get record when snapshot is register, and can't get records after unregister snapshor. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll004, TestSize.Level2) +{ + /** + * @tc.steps: step1. put (k1, v1), update (k1, v1) to (k1, v2). + * @tc.expected: step1. put and update successfully. + */ + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_kvBackupDelegate->Put(KEY_1, VALUE_2), OK); + KvStoreObserverImpl observer; + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::RegisterSnapObserver(g_kvBackupDelegate, &observer); + EXPECT_NE(snapShot, nullptr); + /** + * @tc.steps: step2. delete k1 and then check data of k1; + * @tc.expected: step2. there is only one records in the db. + */ + EXPECT_EQ(g_kvBackupDelegate->Delete(KEY_1), OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + std::string identifier = g_kvdbParameter1.userId + "-" + g_kvdbParameter1.appId + "-" + g_kvdbParameter1.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string kvDbName = DIRECTOR + hashIdentifierRes + KVMULTIDB; + + std::vector keyS; + keyS.push_back(KEY_1); + int count = 0; + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), QUERY_SQL, keyS, g_kvOption, count); + EXPECT_EQ(count, ONE_RECORD); + + /** + * @tc.steps: step3. after release the snap shot query the records; + * @tc.expected: step3. can't find the k1 in db. + */ + EXPECT_EQ(g_kvBackupDelegate->ReleaseKvStoreSnapshot(snapShot), OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + DistributedTestTools::GetRecordCntByKey(kvDbName.c_str(), QUERY_SQL, keyS, g_kvOption, count); + EXPECT_TRUE(count == 0); +} + +/* + * @tc.name: DeleteAll 006 + * @tc.desc: value slices will be real deleted if reference count is 0 caused by clear. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll006, TestSize.Level2) +{ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + /** + * @tc.steps: step1. put (k1,v1) and (k2,v2), each value of them are 4M. + * @tc.expected: step1. call successfully. + */ + EntrySize entrySize = { KEY_SIX_BYTE, FOUR_M_LONG_STRING }; + std::vector entries; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, RECORDS_SMALL_CNT, { 'K' }, { 'v' }); + for (auto entry : entries) { + EXPECT_EQ(delegate2->Put(entry.key, entry.value), OK); + } + /** + * @tc.steps: step2. clear and verify the records of ValueSlice by sqlite interface. + * @tc.expected: step1. count is 0. + */ + EXPECT_EQ(delegate2->Clear(), OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + std::string identifier = g_kvdbParameter2.userId + "-" + g_kvdbParameter2.appId + + "-" + g_kvdbParameter2.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string dbName = DIRECTOR + hashIdentifierRes + MULTIDB; + int count = 0; + EncrypteAttribute attribute = {g_kvOption.isEncryptedDb, g_kvOption.passwd}; + EXPECT_TRUE(DistributedTestTools::QuerySpecifiedData(dbName, SYNC_VALUE_SLICE_QUERY_SQL, attribute, count)); + EXPECT_EQ(count, 0); // there are no ValueSlices in db. + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: DeleteAll 007 + * @tc.desc: value slices will be real deleted if reference count is 0 caused by clear. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbKvRealdelTest, KvDeleteAll007, TestSize.Level2) +{ + KvStoreDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedTestTools::GetDelegateSuccess(manager2, g_kvdbParameter2, g_kvOption); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + /** + * @tc.steps: step1. put (k1,v1) and (k2,v1), each value of them are 4M. + * @tc.expected: step1. call successfully. + */ + EntrySize entrySize = { KEY_SIX_BYTE, FOUR_M_LONG_STRING }; + std::vector entries; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, RECORDS_SMALL_CNT, { 'K' }, { 'v' }); + entries[INDEX_FIRST].value = entries[INDEX_ZEROTH].value; + for (auto entry : entries) { + EXPECT_EQ(delegate2->Put(entry.key, entry.value), OK); + } + /** + * @tc.steps: step2. delete k1 and verify the records of ValueSlice by sqlite interface. + * @tc.expected: step1. count is not equal to 0. + */ + std::string identifier = g_kvdbParameter2.userId + "-" + g_kvdbParameter2.appId + + "-" + g_kvdbParameter2.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string dbName = DIRECTOR + hashIdentifierRes + MULTIDB; + EXPECT_EQ(delegate2->Delete(entries[INDEX_ZEROTH].key), OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + int count = 0; + EncrypteAttribute attribute = {g_kvOption.isEncryptedDb, g_kvOption.passwd}; + EXPECT_TRUE(DistributedTestTools::QuerySpecifiedData(dbName, SYNC_VALUE_SLICE_QUERY_SQL, attribute, count)); + EXPECT_EQ(count, 0); // there are some ValueSlices in db. + /** + * @tc.steps: step3. delete k2 and verify the records of ValueSlice by sqlite interface. + * @tc.expected: step1. count is 0. + */ + EXPECT_EQ(delegate2->Delete(entries[INDEX_FIRST].key), OK); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + count = 0; + EXPECT_TRUE(DistributedTestTools::QuerySpecifiedData(dbName, SYNC_VALUE_SLICE_QUERY_SQL, attribute, count)); + EXPECT_EQ(count, 0); // there are no ValueSlices in db. + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_perf_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_perf_test.cpp new file mode 100755 index 000000000..883e1172a --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_perf_test.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_generator.h" +#include "distributed_test_tools.h" +#include "distributed_crud_transaction_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvTransactionPerf { +const bool IS_LOCAL = false; +const int PUT_TIMES = 10; +const int KEY_LENGTH = 16; +const int VALUE_LENGTH = 100; + +class DistributeddbKvTransactionPerfTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +KvStoreDelegate *g_transactionDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_transactionManager = nullptr; +void DistributeddbKvTransactionPerfTest::SetUpTestCase(void) +{ +} + +void DistributeddbKvTransactionPerfTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvTransactionPerfTest::SetUp(void) +{ + MST_LOG("SetUpTestCase before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_transactionDelegate = DistributedTestTools::GetDelegateSuccess(g_transactionManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_transactionManager != nullptr && g_transactionDelegate != nullptr); +} + +void DistributeddbKvTransactionPerfTest::TearDown(void) +{ + EXPECT_EQ(g_transactionManager->CloseKvStore(g_transactionDelegate), OK); + g_transactionDelegate = nullptr; + DBStatus status = g_transactionManager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_transactionManager; + g_transactionManager = nullptr; + RemoveDir(DIRECTOR); +} + +/* + * @tc.name: Performance 001 + * @tc.desc: Compare between inserting many records in one transaction and one record in each transaction. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance001, TestSize.Level3) +{ + /** + * @tc.steps: step1.get the putDuration of putBatch(keys,values) by transaction. + * @tc.expected: step1. the putDuration is smaller than 1ms/k. + */ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, false, IS_LOCAL); + double transactionInsert, singleInsert, batchInsert; + + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + transactionInsert = performance.putDuration; + /** + * @tc.steps: step2.get the putDuration of put(k,v) singlely. + * @tc.expected: step2. the putDuration is smaller than 1ms/k and bigger than step1. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + singleInsert = performance.putDuration; + /** + * @tc.steps: step3.get the putDuration of putBatch(keys,value) without transaction. + * @tc.expected: step3. the putDuration is smaller than 1ms/k. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + batchInsert = performance.putDuration; + + if (transactionInsert <= batchInsert && batchInsert <= singleInsert) { + MST_LOG("the put performance is ok!"); + } +} + +/* + * @tc.name: Performance 002 + * @tc.desc: Compare between updating many records in one transaction and one record in each transaction. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance002, TestSize.Level3) +{ + /** + * @tc.steps: step1.get the putDuration of updateBatch(keys,values) by transaction. + * @tc.expected: step1. the putDuration is smaller than 1ms/k. + */ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, false, IS_LOCAL); + double transactionUpdate, singleUpdate, batchUpdate; + + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + transactionUpdate = performance.updateDuration; + /** + * @tc.steps: step2.get the putDuration of update(k,v) singlely. + * @tc.expected: step2. the putDuration is smaller than 1ms/k and is bigger than step1. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + singleUpdate = performance.updateDuration; + /** + * @tc.steps: step3.get the putDuration of putBatch(keys,value) without transaction. + * @tc.expected: step3. the putDuration is smaller than 1ms/k. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + batchUpdate = performance.updateDuration; + + if (transactionUpdate <= batchUpdate && batchUpdate <= singleUpdate) { + MST_LOG("the update performance is ok!"); + } +} + +/* + * @tc.name: Performance 003 + * @tc.desc: Compare between deleting many records in one transaction and one record in each transaction. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance003, TestSize.Level3) +{ + /** + * @tc.steps: step1.get the putDuration of deleteBatch(keys,values) by transaction. + * @tc.expected: step1. the putDuration is smaller than 1ms/k. + */ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, false, IS_LOCAL); + double transactionDel, singleDel, batchDel; + + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + transactionDel = performance.deleteDuration; + /** + * @tc.steps: step2.get the putDuration of delete(k,v) singlely. + * @tc.expected: step2. the putDuration is smaller than 1ms/k and is bigger than step1. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + singleDel = performance.deleteDuration; + /** + * @tc.steps: step3.get the putDuration of deleteBatch(keys,value) without transaction. + * @tc.expected: step3. the putDuration is smaller than 1ms/k. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + batchDel = performance.deleteDuration; + + if (transactionDel <= batchDel && batchDel <= singleDel) { + MST_LOG("the delete performance is ok!"); + } +} + +/* + * @tc.name: Performance 004 + * @tc.desc: system info of inserting many records in one transaction and one record in each transaction. + * @tc.type: Performance + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance004, TestSize.Level3) +{ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, true, IS_LOCAL); + /** + * @tc.steps: step1.get the sys info of putBatch(keys,values) by transaction. + * @tc.expected: step1. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + /** + * @tc.steps: step2.get the sys info of put(k,v) singlely. + * @tc.expected: step2. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + /** + * @tc.steps: step3.get the sys info of putBatch(keys,values) without transaction. + * @tc.expected: step3. the sys info is normal. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); +} + +/* + * @tc.name: Performance 005 + * @tc.desc: system info of updating many records in one transaction and one record in each transaction. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance005, TestSize.Level3) +{ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, true, IS_LOCAL); + + /** + * @tc.steps: step1.get the sys info of updateBatch(keys,values) by transaction. + * @tc.expected: step1. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + /** + * @tc.steps: step2.get the sys info of update(k,v) singlely. + * @tc.expected: step2. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + /** + * @tc.steps: step3.get the sys info of updateBatch(keys,values) without transaction. + * @tc.expected: step3. the sys info is normal. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); +} + +/* + * @tc.name: Performance 006 + * @tc.desc: system info of deleting many records in one transaction and one record in each transaction. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionPerfTest, Performance006, TestSize.Level3) +{ + PerformanceData performance(PUT_TIMES, KEY_LENGTH, VALUE_LENGTH, false, false, false, true, IS_LOCAL); + + /** + * @tc.steps: step1.get the sys info of deleteBatch(keys,values) by transaction. + * @tc.expected: step1. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateTransactionPerformance(performance)); + /** + * @tc.steps: step2.get the sys info of delete(k,v) singlely. + * @tc.expected: step2. the sys info is normal. + */ + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); + /** + * @tc.steps: step3.get the sys info of deleteBatch(keys,values) without transaction. + * @tc.expected: step3. the sys info is normal. + */ + performance.putBatch = true; + EXPECT_TRUE(DistributedTestTools::CalculateOpenPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateInsertPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetPutPerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateGetUpdatePerformance(performance)); + EXPECT_TRUE(DistributedTestTools::CalculateUseClearPerformance(performance)); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_test.cpp new file mode 100755 index 000000000..abe3b8617 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_kv_transaction_test.cpp @@ -0,0 +1,1998 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" +#include "distributed_crud_transaction_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_delegate_manager.h" +#include "types.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbKvTransaction { +const bool IS_LOCAL = false; +const int REPEAT_TIMES = 5; +const long SLEEP_WHEN_CONSISTENCY_NOT_FINISHED = 20000; +const long SLEEP_ISOLATION_TRANSACTION = 5; +const unsigned long WAIT_FOR_CHECKING_SECONDS = 1; +const int BASIC_ACID_RUN_TIME = 1; + +class DistributeddbKvTransactionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +KvStoreDelegate *g_transactionDelegate = nullptr; // the delegate used in this suit. +KvStoreDelegateManager *g_transactionManager = nullptr; +void DistributeddbKvTransactionTest::SetUpTestCase(void) +{ +} + +void DistributeddbKvTransactionTest::TearDownTestCase(void) +{ +} + +void DistributeddbKvTransactionTest::SetUp(void) +{ + MST_LOG("SetUpTestCase before all cases local[%d].", IS_LOCAL); + RemoveDir(DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_transactionDelegate = DistributedTestTools::GetDelegateSuccess(g_transactionManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_transactionManager != nullptr && g_transactionDelegate != nullptr); +} + +void DistributeddbKvTransactionTest::TearDown(void) +{ + EXPECT_EQ(g_transactionManager->CloseKvStore(g_transactionDelegate), OK); + g_transactionDelegate = nullptr; + DBStatus status = g_transactionManager->DeleteKvStore(STORE_ID_1); + EXPECT_EQ(status, DistributedDB::DBStatus::OK) << "fail to delete exist kvdb"; + delete g_transactionManager; + g_transactionManager = nullptr; + RemoveDir(DIRECTOR); +} + +/* + * @tc.name: BasicAction 001 + * @tc.desc: Verify that can start and commit a transaction successfully. + * @tc.type: FUNC + * @tc.require: SR000CQDTL + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction001, TestSize.Level0) +{ + /** + * @tc.steps: step1. start transaction . + * @tc.expected: step1. start transaction successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + EXPECT_TRUE(g_transactionDelegate->Put(KEY_1, VALUE_1) == DBStatus::OK); + EXPECT_TRUE(g_transactionDelegate->Put(KEY_2, VALUE_2) == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + /** + * @tc.steps: step2. commit transaction. + * @tc.expected: step2. commit transaction successfully. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); +} + +/* + * @tc.name: BasicAction 002 + * @tc.desc: Verify that can start and rollback a transaction successfully. + * @tc.type: FUNC + * @tc.require: SR000CQDTL + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction002, TestSize.Level0) +{ + /** + * @tc.steps: step1. start transaction and Put (k1, v1) and check the record. + * @tc.expected: step1. start transaction successfully and can't find the record in db. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + EXPECT_TRUE(g_transactionDelegate->Put(KEY_1, VALUE_1) == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + /** + * @tc.steps: step2. rollback transaction and check the recordd of (k1, v1). + * @tc.expected: step2. rollback transaction successfully and can't find the record in db still. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: BasicAction 003 + * @tc.desc: Verify that can not start the same transaction repeatedly. + * @tc.type: FUNC + * @tc.require: SR000CQDTL + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction003, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transaction successfully. + */ + DBStatus status = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step2. start the same transaction again. + * @tc.expected: step2. start transaction failed. + */ + status = g_transactionDelegate->StartTransaction(); + EXPECT_EQ(status, DBStatus::DB_ERROR); + + status = g_transactionDelegate->Rollback(); + EXPECT_TRUE(status == DBStatus::OK); +} + +/* + * @tc.name: BasicAction 004 + * @tc.desc: Verify that can not commit transaction without starting it. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction004, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction then rollback. + * @tc.expected: step1. Construct that has no transaction start. + */ + EXPECT_EQ(DBStatus::OK, g_transactionDelegate->StartTransaction()); + EXPECT_EQ(DBStatus::OK, g_transactionDelegate->Rollback()); + /** + * @tc.steps: step2. commit transaction directly. + * @tc.expected: step2. Commit failed and return errcode correctly. + */ + DBStatus status = g_transactionDelegate->Commit(); + EXPECT_EQ(status, DBStatus::DB_ERROR); +} + +/* + * @tc.name: BasicAction 005 + * @tc.desc: Verify that can not rollback transaction without starting it. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction005, TestSize.Level1) +{ + /** + * @tc.steps: step1. rollback transaction which is not exist at all. + * @tc.expected: step1. Rollback failed and return DB_ERROR. + */ + DBStatus status = g_transactionDelegate->Rollback(); + EXPECT_EQ(status, DBStatus::DB_ERROR); +} + +/* + * @tc.name: BasicAction 006 + * @tc.desc: Verify that can not commit transaction repeatedly. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction006, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_EQ(statusStart, DBStatus::OK); + /** + * @tc.steps: step2. commit transaction. + * @tc.expected: step2. commit successfully. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_EQ(statusCommit, DBStatus::OK); + /** + * @tc.steps: step3. commit transaction again. + * @tc.expected: step3. commit failed. + */ + statusCommit = g_transactionDelegate->Commit(); + EXPECT_EQ(statusCommit, DBStatus::DB_ERROR); +} + +/* + * @tc.name: BasicAction 007 + * @tc.desc: Verify that can not rollback transaction after committing it. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction007, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. commit transaction. + * @tc.expected: step2. commit successfully. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + /** + * @tc.steps: step3. rollback transaction. + * @tc.expected: step3. rollback failed. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback != DBStatus::OK); +} + +/* + * @tc.name: BasicAction 008 + * @tc.desc: Verify that can not commit transaction after rollabcking it. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction008, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. rollback transaction. + * @tc.expected: step2. rollback successfully. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + /** + * @tc.steps: step3. commit transaction. + * @tc.expected: step3. commit failed. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_EQ(statusCommit, DBStatus::DB_ERROR); +} + +/* + * @tc.name: BasicAction 009 + * @tc.desc: Verify that can not rollback transaction repeatedly. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction009, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. rollback transaction. + * @tc.expected: step2. rollback successfully. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + /** + * @tc.steps: step3. rollback transaction again. + * @tc.expected: step3. rollback failed. + */ + statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback != DBStatus::OK); +} + +/* + * @tc.name: BasicAction 0010 + * @tc.desc: Verify that can start and commit, then start and rollback repeatedly. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicAction010, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction and put (k1, v1) to db and commit, + * then start and put (k2, v2) to db and rollback it for 5 times, and at the end check the data in db. + * @tc.expected: step1. Operate successfully every time and (k1, v1) is in db and (k2, v2) is not. + */ + for (int time = 0; time < REPEAT_TIMES; ++time) { + DBStatus statusStart1 = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(g_transactionDelegate->Put(KEY_1, VALUE_1) == DBStatus::OK); + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusStart1 == DBStatus::OK); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + DBStatus statusStart2 = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(g_transactionDelegate->Put(KEY_2, VALUE_2) == DBStatus::OK); + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusStart2 == DBStatus::OK); + EXPECT_TRUE(statusRollback == DBStatus::OK); + } + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: Crud 001 + * @tc.desc: Verify that can insert and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. put(k1,v1) to db. + * @tc.expected: step2. put successfully. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step3. commit transaction and get. + * @tc.expected: step3. commit successfully and get v1 of k1. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); +} + +/* + * @tc.name: Crud 002 + * @tc.desc: Verify that can update and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud002, TestSize.Level1) +{ + /** + * @tc.steps: step1.put(k1,v1) to db. + * @tc.expected: step1. put successfully to construct exist data in db. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(statusPut == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + ASSERT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + /** + * @tc.steps: step2. start transaction. + * @tc.expected: step2. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step3. update (k1,v1) to (k1,v2). + * @tc.expected: step3. update successfully. + */ + statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get v2 of k1. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + ASSERT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); +} + +/* + * @tc.name: Crud 003 + * @tc.desc: Verify that can delete and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud003, TestSize.Level0) +{ + /** + * @tc.steps: step1.put(k1,v1) to db and get. + * @tc.expected: step1. put successfully get v1 of k1. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + ASSERT_TRUE(statusPut == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + ASSERT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + /** + * @tc.steps: step2. start transaction. + * @tc.expected: step2. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step3. delete (k1) from db. + * @tc.expected: step3. delete successfully. + */ + statusPut = DistributedTestTools::Delete(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get null. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() == 0); +} + +/* + * @tc.name: Crud 004 + * @tc.desc: Verify that can insert patch and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud004, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step2. putBatch successfully. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step3. commit transaction and get. + * @tc.expected: step3. commit successfully and get v1 of k1, v2 of k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); +} + +/* + * @tc.name: Crud 005 + * @tc.desc: Verify that can update patch and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud005, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries1Up; + entries1Up.push_back(ENTRY_1_2); + entries1Up.push_back(ENTRY_2_3); + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) to db and get. + * @tc.expected: step1. putBatch successfully and get v1 of k1, v2 of k2. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + /** + * @tc.steps: step2. start transaction. + * @tc.expected: step2. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step3. update (k1,v1)(k2,v2) to (k1,v2)(k2,v3). + * @tc.expected: step3. start successfully. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1Up); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get v2 of k1, v3 of k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_3)); +} + +/* + * @tc.name: Crud 006 + * @tc.desc: Verify that can delete patch and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud006, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) to db and get. + * @tc.expected: step1. putBatch successfully and get v1 of k1, v2 of k2. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(value.size() != 0); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); + /** + * @tc.steps: step2. start transaction. + * @tc.expected: step2. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step3. deleteBatch (k1)(k2). + * @tc.expected: step3. deleteBatch successfully. + */ + DBStatus statusPut = DistributedTestTools::DeleteBatch(*g_transactionDelegate, keys1); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get null of k1,k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(value.size() == 0); +} + +/* + * @tc.name: Crud 007 + * @tc.desc: Verify that can clear and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud007, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + /** + * @tc.steps: step1. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step1. putBatch successfully. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step2. start transaction. + * @tc.expected: step2. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step3. clear db. + * @tc.expected: step3. clear successfully. + */ + DBStatus statusPut = DistributedTestTools::Clear(*g_transactionDelegate); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get null of k1,k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + Value valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(valueResult.size() == 0); + valueResult = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(valueResult.size() == 0); +} + +/* + * @tc.name: Crud 008 + * @tc.desc: Verify that can insert then update and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud008, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. put (k1,v1) to db. + * @tc.expected: step2. put successfully. + */ + DBStatus statusPut1 = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(statusPut1 == DBStatus::OK); + /** + * @tc.steps: step3. update (k1,v1) to (k1,v2). + * @tc.expected: step3. update successfully. + */ + DBStatus statusPut2 = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut2 == DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and get. + * @tc.expected: step4. commit successfully and get v1 of k1, v2 of k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); +} + +/* + * @tc.name: Crud 009 + * @tc.desc: Verify that can complex update and commit a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Crud009, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_3); + vector keys2; + keys2.push_back(ENTRY_3.key); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus startStatus = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(startStatus == DBStatus::OK); + /** + * @tc.steps: step2. clear db. + * @tc.expected: step2. clear successfully. + */ + DBStatus statusClear = DistributedTestTools::Clear(*g_transactionDelegate); + EXPECT_TRUE(statusClear == DBStatus::OK); + /** + * @tc.steps: step3. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step3. putBatch successfully. + */ + DBStatus putBatchStatus1 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(putBatchStatus1 == DBStatus::OK); + /** + * @tc.steps: step4. putBatch (k3,v3) to db. + * @tc.expected: step4. putBatch successfully. + */ + DBStatus putBatchStatus2 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(putBatchStatus2 == DBStatus::OK); + /** + * @tc.steps: step5. deleteBatch entries2 from db. + * @tc.expected: step5. deleteBatch successfully. + */ + DBStatus statusDeleteBatch2 = DistributedTestTools::DeleteBatch(*g_transactionDelegate, keys2); + EXPECT_TRUE(statusDeleteBatch2 == DBStatus::OK); + /** + * @tc.steps: step6. delete(k1)(k2) from db. + * @tc.expected: step6. delete successfully. + */ + DBStatus statusDelete1 = DistributedTestTools::Delete(*g_transactionDelegate, ENTRY_1.key); + EXPECT_TRUE(statusDelete1 == DBStatus::OK); + DBStatus statusDelete2 = DistributedTestTools::Delete(*g_transactionDelegate, ENTRY_2.key); + EXPECT_TRUE(statusDelete2 == DBStatus::OK); + /** + * @tc.steps: step7. put(k1,v2) to db. + * @tc.expected: step7. put successfully. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, ENTRY_1_2.key, ENTRY_1_2.value); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step8. commit transaction and get. + * @tc.expected: step8. commit successfully and get v2 of k1, null of k2,k3. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_3); + EXPECT_TRUE(value.size() == 0); +} + +/* + * @tc.name: Pressure 001 + * @tc.desc: Verify that insert invalid key and commit a transaction will fail. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Pressure001, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step2. putBatch successfully. + */ + DBStatus statusPutBatch1 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries); + EXPECT_TRUE(statusPutBatch1 == DBStatus::OK); + /** + * @tc.steps: step3. put (null,null) to db. + * @tc.expected: step3. put failed. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_EMPTY, VALUE_1); + EXPECT_TRUE(statusPut != DBStatus::OK); + /** + * @tc.steps: step4. commit transaction and check the data on db. + * @tc.expected: step4. commit successfully and there are only 2 records and equal to entries. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + + std::vector entriesGot = DistributedTestTools::GetEntries(*g_transactionDelegate, KEY_EMPTY); + EXPECT_EQ(entriesGot.size(), entries.size()); + EXPECT_TRUE(CompareEntriesVector(entriesGot, entries)); +} + +/* + * @tc.name: Pressure 005 + * @tc.desc: Verify that support complex update and rollback a transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Pressure005, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries2; + entries2.push_back(ENTRY_3); + vector keys2; + keys2.push_back(ENTRY_3.key); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. clear db. + * @tc.expected: step2. clear successfully. + */ + DBStatus statusClear = DistributedTestTools::Clear(*g_transactionDelegate); + EXPECT_TRUE(statusClear == DBStatus::OK); + /** + * @tc.steps: step3. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step3. putBatch successfully. + */ + DBStatus statusPutBatch1 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(statusPutBatch1 == DBStatus::OK); + /** + * @tc.steps: step4. putBatch (k3,v3) to db. + * @tc.expected: step4. putBatch successfully. + */ + DBStatus statusPutBatch2 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(statusPutBatch2 == DBStatus::OK); + /** + * @tc.steps: step5. deleteBatch (k3) to db. + * @tc.expected: step5. deleteBatch successfully. + */ + DBStatus statusDeleteBatch2 = DistributedTestTools::DeleteBatch(*g_transactionDelegate, keys2); + EXPECT_TRUE(statusDeleteBatch2 == DBStatus::OK); + /** + * @tc.steps: step6. delete (k1)(k2) from db. + * @tc.expected: step6. delete successfully. + */ + DBStatus statusDelete1 = DistributedTestTools::Delete(*g_transactionDelegate, ENTRY_1.key); + EXPECT_TRUE(statusDelete1 == DBStatus::OK); + DBStatus statusDelete2 = DistributedTestTools::Delete(*g_transactionDelegate, ENTRY_2.key); + EXPECT_TRUE(statusDelete2 == DBStatus::OK); + /** + * @tc.steps: step6. put (k1,v2) to db again. + * @tc.expected: step6. put successfully. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step6. rollback transaction and get. + * @tc.expected: step6. rollback successfully and get null of k1,k2,k3. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + std::vector entriesGot = DistributedTestTools::GetEntries(*g_transactionDelegate, KEY_EMPTY); + EXPECT_TRUE(entriesGot.empty()); +} + +/* + * @tc.name: Pressure 006 + * @tc.desc: Verify that insert invalid key and rollback a transaction successfully. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Pressure006, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step2. putBatch successfully. + */ + DBStatus statusPutBatch1 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(statusPutBatch1 == DBStatus::OK); + /** + * @tc.steps: step3. put (k,v) that k=null to db. + * @tc.expected: step3. put failed. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_EMPTY, VALUE_1); + EXPECT_TRUE(statusPut != DBStatus::OK); + /** + * @tc.steps: step4. rollback transaction and get. + * @tc.expected: step4. rollback successfully and get null of k1,k2. + */ + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + std::vector entriesGot = DistributedTestTools::GetEntries(*g_transactionDelegate, KEY_EMPTY); + EXPECT_TRUE(entriesGot.empty()); +} + +/* + * @tc.name: Pressure 007 + * @tc.desc: Verify that start a transaction and close conn before rollback will result in transaction is invalid. + * @tc.type: Fault injection + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Pressure007, TestSize.Level1) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter2_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + vector entries; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = delegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step2. putBatch successfully. + */ + DBStatus statusPutBatch = DistributedTestTools::PutBatch(*delegate, entries); + EXPECT_TRUE(statusPutBatch == DBStatus::OK); + /** + * @tc.steps: step3. close db before rollback transaction. + * @tc.expected: step3. close successfully. + */ + DBStatus closeStatus = manager->CloseKvStore(delegate); + EXPECT_TRUE(closeStatus == DBStatus::OK); + delegate = nullptr; + delete manager; + manager = nullptr; + /** + * @tc.steps: step4. rollback transaction and get. + * @tc.expected: step4. rollback failed and get null of k1,k2. + */ + delegate = DistributedTestTools::GetDelegateSuccess(manager, + g_kvdbParameter2_1_2, g_kvOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + DBStatus statusRollback = delegate->Rollback(); + EXPECT_TRUE(statusRollback != DBStatus::OK); + if (delegate == nullptr) { + MST_LOG("delegate nullptr"); + } + Value value = DistributedTestTools::Get(*delegate, KEY_1); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*delegate, KEY_2); + EXPECT_TRUE(value.size() == 0); + + closeStatus = manager->CloseKvStore(delegate); + EXPECT_TRUE(closeStatus == DBStatus::OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: Acid 001 + * @tc.desc: Verify that single action's atomic. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid001, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + DBStatus status = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. update (k1,v1) to (k2,v2). + * @tc.expected: step2. update successfully. + */ + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_2); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step3. get (k1). + * @tc.expected: step3. get v1 of k1. + */ + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + /** + * @tc.steps: step4. delete (k1). + * @tc.expected: step4. delete successfully. + */ + DBStatus statusDelete1 = DistributedTestTools::Delete(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(statusDelete1 == DBStatus::OK); + /** + * @tc.steps: step5. get (k1). + * @tc.expected: step5. get v1 of k1. + */ + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + /** + * @tc.steps: step6. put (k2,v2) to db. + * @tc.expected: step6. put successfully. + */ + DBStatus statusPut2 = DistributedTestTools::Put(*g_transactionDelegate, KEY_2, VALUE_2); + EXPECT_TRUE(statusPut2 == DBStatus::OK); + /** + * @tc.steps: step7. get (k2). + * @tc.expected: step7. get null of k2. + */ + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(value.size() == 0); + /** + * @tc.steps: step7. commit transaction and get. + * @tc.expected: step7. get null of k1, v2 of k2. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); +} + +/* + * @tc.name: Acid 002 + * @tc.desc: Verify that batch action's atomic. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid002, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + vector entries1, entries2, entries1Up; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_3); + entries1Up.push_back(ENTRY_1_2); + entries1Up.push_back(ENTRY_2_3); + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. updateBatch (k1,v2) to (k2,v3). + * @tc.expected: step2. updateBatch successfully. + */ + DBStatus statusPut = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1Up); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step3. Get (k1)(k2). + * @tc.expected: step3. get v1 of k1, v2 of k2. + */ + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); + /** + * @tc.steps: step4. DeleteBatch (k1)(k2). + * @tc.expected: step4. DeleteBatch successfully. + */ + DBStatus statusDelete1 = DistributedTestTools::DeleteBatch(*g_transactionDelegate, keys1); + EXPECT_TRUE(statusDelete1 == DBStatus::OK); + /** + * @tc.steps: step5. Get. + * @tc.expected: step5. get v1 of k1, v2 of k2. + */ + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_1)); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_2)); + /** + * @tc.steps: step6. putBatch (k3,v3) to db. + * @tc.expected: step6. putBatch successfully. + */ + DBStatus statusPut2 = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(statusPut2 == DBStatus::OK); + /** + * @tc.steps: step7. get (k3). + * @tc.expected: step7. get null 0f k3. + */ + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_3); + EXPECT_TRUE(value.size() == 0); + /** + * @tc.steps: step8. commit transaction and get. + * @tc.expected: step8. get null of k1,k2, v3 of k3. + */ + DBStatus statusCommit = g_transactionDelegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_2); + EXPECT_TRUE(value.size() == 0); + value = DistributedTestTools::Get(*g_transactionDelegate, KEY_3); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(value, VALUE_3)); +} + +int g_singleThreadComplete = 0; +bool g_singleThreadSuccess = true; +void ConsistencyCheck(Key kLeft, Key kRight) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr); + + while (g_singleThreadComplete < static_cast(SINGLE_THREAD_NUM)) { + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::GetKvStoreSnapshot(*delegate); + if (snapShot == nullptr) { + MST_LOG("ConsistencyCheck get snapShot failed."); + return; + } + Value left = DistributedTestTools::Get(*snapShot, kLeft); + Value right = DistributedTestTools::Get(*snapShot, kRight); + MST_LOG("ConsistencyCheck get sum %d,%d.", GetIntValue(left), GetIntValue(right)); + if (GetIntValue(left) + GetIntValue(right) != VALUE_SUM) { + g_singleThreadSuccess = false; + MST_LOG("ConsistencyCheck get sum %d,%d failed.", GetIntValue(left), GetIntValue(right)); + break; + } + EXPECT_EQ(delegate->ReleaseKvStoreSnapshot(snapShot), OK); + snapShot = nullptr; + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + } + + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +void ConsistencyBusiness(Key kLeft, Value vLeft, Key kRight, Value vRight) +{ + // wait 100 ms, let ConsistencyCheck begin + std::this_thread::sleep_for(std::chrono::duration(HUNDRED_MILLI_SECONDS)); + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr); + + MST_LOG("ConsistencyBusiness put %d,%d.", GetIntValue(vLeft), GetIntValue(vRight)); + DBStatus statusStart = delegate->StartTransaction(); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + DBStatus statusPutLeft = delegate->Put(kLeft, vLeft); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + DBStatus statusPutRight = delegate->Put(kRight, vRight); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + DBStatus statusCommit = delegate->Commit(); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + if (statusPutLeft != DBStatus::OK || statusPutRight != DBStatus::OK || + statusStart != DBStatus::OK || statusCommit != DBStatus::OK) { + MST_LOG("ConsistencyBusiness put failed."); + g_singleThreadComplete++; + g_singleThreadSuccess = false; + return; + } + + g_singleThreadComplete++; + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: Acid 003 + * @tc.desc: Verify that single action's consistency. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid003, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + + int baseNumer = VALUE_FIVE_HUNDRED; + Value base = GetValueWithInt(baseNumer); + + Entry entry1, entry2; + vector entries1; + entry1 = {KEY_CONS_1, base}; + entry2 = {KEY_CONS_2, base}; + entries1.push_back(entry1); + entries1.push_back(entry2); + /** + * @tc.steps: step1. putBatch (k1=cons1,v1=500) (k2=cons2,v2=500). + * @tc.expected: step1. putBatch successfully to construct exist v1+v2=1000. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + Value value = DistributedTestTools::Get(*g_transactionDelegate, entry1.key); + EXPECT_EQ(GetIntValue(value), baseNumer); + value = DistributedTestTools::Get(*g_transactionDelegate, entry2.key); + EXPECT_EQ(GetIntValue(value), baseNumer); + + /** + * @tc.steps: step2. start a thread to update k1=cons1's value to 400, update k2=cons2's value to 600. + * @tc.expected: step2. run thread successfully and k1+k2=1000 is true in child thread. + */ + std::vector bussinessThreads; + bussinessThreads.push_back(std::thread(ConsistencyBusiness, KEY_CONS_1, GetValueWithInt(VALUE_CHANGE1_FIRST), + KEY_CONS_2, GetValueWithInt(VALUE_CHANGE1_SECOND))); + /** + * @tc.steps: step3. start another thread to update k1=cons1's value to 700, update k2=cons2's value to 300. + * @tc.expected: step3. run thread successfully and k1+k2=1000 is true in child thread. + */ + bussinessThreads.push_back(std::thread(ConsistencyBusiness, KEY_CONS_1, GetValueWithInt(VALUE_CHANGE2_FIRST), + KEY_CONS_2, GetValueWithInt(VALUE_CHANGE2_SECOND))); + for (auto& th : bussinessThreads) { + th.detach(); + } + + ConsistencyCheck(KEY_CONS_1, KEY_CONS_2); + ASSERT_TRUE(g_singleThreadSuccess); +} + +bool g_batchThreadSuccess = true; +bool g_batchThreadComplete = false; +void ConsistencyBatchCheck(vector *kLeft, vector *kRight, int size) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr); + + while (!g_batchThreadComplete) { + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::GetKvStoreSnapshot(*delegate); + if (snapShot == nullptr) { + MST_LOG("ConsistencyBatchCheck get snapShot failed."); + return; + } + for (int i = 0; i < size; ++i) { + Value left = DistributedTestTools::Get(*snapShot, kLeft->at(i)); + Value right = DistributedTestTools::Get(*snapShot, kRight->at(i)); + if (GetIntValue(left) + GetIntValue(right) != size) { + g_batchThreadSuccess = false; + MST_LOG("ConsistencyBatchCheck get sum %d failed.", size); + break; + } + } + EXPECT_EQ(delegate->ReleaseKvStoreSnapshot(snapShot), OK); + snapShot = nullptr; + } + + MST_LOG("g_batchThreadComplete."); + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +void TransactionCheckConsistency(vector &entries1, vector &allKeys1, + vector &entries2, vector &allKeys2) +{ + Key query1 = {'k', 'a'}; + Key query2 = {'k', 'b'}; + /** + * @tc.steps: step1. putBatch 20 items of (keys1,values1)(key2,values2). + * @tc.expected: step1. putBatch successfully to construct exist data in db. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(status == DBStatus::OK); + + /** + * @tc.steps: step2. start child thread to query keys1 and keys2 continuously. + * @tc.expected: step2. if keys1.size()+keys2.size()!=20 print info. + */ + thread readThread = thread(ConsistencyBatchCheck, &allKeys1, &allKeys2, TWENTY_RECORDS); + readThread.detach(); + /** + * @tc.steps: step3. start transaction. + * @tc.expected: step3. start transaction successfully. + */ + status = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step4. getEntries with keyprefix before updateBatch. + * @tc.expected: step4. getEntries successfully that the size of them is 20. + */ + vector values1 = DistributedTestTools::GetEntries(*g_transactionDelegate, query1); + vector values2 = DistributedTestTools::GetEntries(*g_transactionDelegate, query2); + ASSERT_EQ(static_cast(values1.size()), TWENTY_RECORDS); + ASSERT_EQ(static_cast(values2.size()), TWENTY_RECORDS); + /** + * @tc.steps: step5. updateBatch values1+1 of keys1, values2-1 of keys2. + * @tc.expected: step5. updateBatch successfully. + */ + for (int i = 0; i != TWENTY_RECORDS; ++i) { + entries1[i].value = GetValueWithInt(GetIntValue(values1[i].value) + 1); + entries2[i].value = GetValueWithInt(GetIntValue(values2[i].value) - 1); + } + status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step6. updateBatch values1+2 of keys1, values2-2 of keys2. + * @tc.expected: step6. updateBatch successfully. + */ + for (int i = 0; i != TWENTY_RECORDS; ++i) { + entries1[i].value = GetValueWithInt(GetIntValue(values1[i].value) + EVEN_NUMBER); + entries2[i].value = GetValueWithInt(GetIntValue(values2[i].value) - EVEN_NUMBER); + } + status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries1); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::PutBatch(*g_transactionDelegate, entries2); + EXPECT_TRUE(status == DBStatus::OK); + /** + * @tc.steps: step7. commit transaction and stop child thread. + * @tc.expected: step7. operate successfully. + */ + status = g_transactionDelegate->Commit(); + EXPECT_TRUE(status == DBStatus::OK); + MST_LOG("after commit"); +} + +/* + * @tc.name: Acid 004 + * @tc.desc: Verify that batch action's consistency. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid004, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + + uint8_t left = 'a'; + uint8_t right = 'b'; + vector entries1; + vector allKeys1; + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys1, entries1); + vector entries2; + vector allKeys2; + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys2, entries2); + for (int i = 0; i < TWENTY_RECORDS; ++i) { + entries1[i].key.insert(entries1[i].key.begin() + 1, left); + entries2[i].key.insert(entries2[i].key.begin() + 1, right); + allKeys1[i].insert(allKeys1[i].begin() + 1, left); + allKeys2[i].insert(allKeys2[i].begin() + 1, right); + entries1[i].value = GetValueWithInt(i); + entries2[i].value = GetValueWithInt(TWENTY_RECORDS - i); + } + + TransactionCheckConsistency(entries1, allKeys1, entries2, allKeys2); + + g_batchThreadComplete = true; + ASSERT_TRUE(g_batchThreadSuccess); + + std::this_thread::sleep_for(std::chrono::duration(MILLSECONDES_PER_SECOND)); +} + +vector g_holdingKeys; +std::mutex g_holdingMutex; +bool CheckKeyHolding(Key applyKey) +{ + std::unique_lock lock(g_holdingMutex); + for (auto key = g_holdingKeys.begin(); key != g_holdingKeys.end(); ++key) { + if (CompareVector(applyKey, *key)) { + string str; + Uint8VecToString(*key, str); + MST_LOG("key %s is hold.", str.c_str()); + return false; + } + } + g_holdingKeys.push_back(applyKey); + return true; +} + +Key GetKey(KvStoreDelegate *&delegate, KvStoreSnapshotDelegate *&snapShot, vector *&allKeys) +{ + Key num = KEY_EMPTY; + Value ticket; + + for (auto ticketNum = allKeys->begin(); ticketNum != allKeys->end(); ++ticketNum) { + ticket = DistributedTestTools::Get(*snapShot, *ticketNum); + if (GetIntValue(ticket) == 1) { + if (!CheckKeyHolding(*ticketNum)) { + continue; + } + num = *ticketNum; + break; + } + } + return num; +} + +void IsolationBussiness(vector *allKeys, Key key) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr); + bool hasTickets = true; + while (hasTickets) { + KvStoreSnapshotDelegate *snapShot = DistributedTestTools::GetKvStoreSnapshot(*delegate); + if (snapShot == nullptr) { + MST_LOG("IsolationBussiness get snapShot failed."); + return; + } + Key num = GetKey(delegate, snapShot, allKeys); + if (num == KEY_EMPTY) { + MST_LOG("IsolationBussiness there have no tickets."); + hasTickets = false; + EXPECT_EQ(delegate->ReleaseKvStoreSnapshot(snapShot), OK); + snapShot = nullptr; + continue; + } + Value result = DistributedTestTools::Get(*snapShot, key); + int resultPlus = GetIntValue(result) + 1; + DBStatus statusStart = delegate->StartTransaction(); + std::this_thread::sleep_for(std::chrono::duration(SLEEP_ISOLATION_TRANSACTION)); + DBStatus statusGetTicket = delegate->Put(num, GetValueWithInt(0)); + DBStatus statusPut = delegate->Put(key, GetValueWithInt(resultPlus)); + std::this_thread::sleep_for(std::chrono::duration(SLEEP_ISOLATION_TRANSACTION)); + DBStatus statusCommit = delegate->Commit(); + std::this_thread::sleep_for(std::chrono::duration(SLEEP_ISOLATION_TRANSACTION)); + if (statusPut != DBStatus::OK || statusGetTicket != DBStatus::OK || statusStart != DBStatus::OK || + statusCommit != DBStatus::OK) { + MST_LOG("IsolationBussiness put failed."); + EXPECT_EQ(delegate->ReleaseKvStoreSnapshot(snapShot), OK); + snapShot = nullptr; + return; + } + + EXPECT_EQ(delegate->ReleaseKvStoreSnapshot(snapShot), OK); + snapShot = nullptr; + } + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: Acid 005 + * @tc.desc: Verify that action's IsolationBussiness. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid005, TestSize.Level3) +{ + EXPECT_EQ(DistributedTestTools::Clear(*g_transactionDelegate), OK); + /** + * @tc.steps: step1. putBatch 20 items of (keys1,values1) where values=1. + * @tc.expected: step1. putBatch successfully to construct exist data in db. + */ + vector entriesBatch; + vector allKeys; + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys, entriesBatch); + vector randomKeys = GetKeysFromEntries(entriesBatch, true); + vector inversionKeys; + inversionKeys.assign(allKeys.rbegin(), allKeys.rend()); + + for (int i = 0; i < TWENTY_RECORDS; ++i) { + entriesBatch[i].value = GetValueWithInt(1); + } + /** + * @tc.steps: step2. start 3 threads to query the items of value=1, if find then item+1. + * @tc.expected: step2. put the result to result1,2,3 of thread1,2,3. + */ + DBStatus status = DistributedTestTools::PutBatch(*g_transactionDelegate, entriesBatch); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::Put(*g_transactionDelegate, KEY_BATCH_CONS_1, GetValueWithInt(0)); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::Put(*g_transactionDelegate, KEY_BATCH_CONS_2, GetValueWithInt(0)); + EXPECT_TRUE(status == DBStatus::OK); + status = DistributedTestTools::Put(*g_transactionDelegate, KEY_BATCH_CONS_3, GetValueWithInt(0)); + EXPECT_TRUE(status == DBStatus::OK); + vector bussinessThreads; + bussinessThreads.push_back(thread(IsolationBussiness, &allKeys, KEY_BATCH_CONS_1)); + bussinessThreads.push_back(thread(IsolationBussiness, &inversionKeys, KEY_BATCH_CONS_2)); + bussinessThreads.push_back(thread(IsolationBussiness, &randomKeys, KEY_BATCH_CONS_3)); + for (auto& th : bussinessThreads) { + th.join(); + } + Value result1 = DistributedTestTools::Get(*g_transactionDelegate, KEY_BATCH_CONS_1); + Value result2 = DistributedTestTools::Get(*g_transactionDelegate, KEY_BATCH_CONS_2); + Value result3 = DistributedTestTools::Get(*g_transactionDelegate, KEY_BATCH_CONS_3); + /** + * @tc.steps: step3. calculate the sum of result1,result2 and result3. + * @tc.expected: step3. the sum is equle 20. + */ + int sum = GetIntValue(result1) + GetIntValue(result2) + GetIntValue(result3); + MST_LOG("sum %d", sum); + ASSERT_EQ(sum, TWENTY_RECORDS); + std::this_thread::sleep_for(std::chrono::duration(SLEEP_WHEN_CONSISTENCY_NOT_FINISHED)); + g_holdingKeys.clear(); +} + +/* + * @tc.name: Acid 006 + * @tc.desc: Verify that action's PersistenceBussiness. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, Acid006, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr); + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = delegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. putBatch (k1,v1)(k2,v2) to db. + * @tc.expected: step2. putBatch successfully. + */ + DBStatus statusPut = delegate->PutBatch(entries1); + EXPECT_TRUE(statusPut == DBStatus::OK); + /** + * @tc.steps: step3. commit transaction. + * @tc.expected: step3. commit successfully. + */ + DBStatus statusCommit = delegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + /** + * @tc.steps: step4. close kv db then open a new db. + * @tc.expected: step4. operate successfully. + */ + DBStatus statusClose = delegateManager->CloseKvStore(delegate); + EXPECT_TRUE(statusClose == DBStatus::OK); + delegate = nullptr; + KvStoreDelegate *delegateNew = nullptr; + KvStoreDelegateManager *delegateManagerNew = nullptr; + delegateNew = DistributedTestTools::GetDelegateSuccess(delegateManagerNew, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManagerNew != nullptr && delegateNew != nullptr) << "fail to create exist kvdb"; + /** + * @tc.steps: step5. get (k1)(k2). + * @tc.expected: step5. get v1 of k1, v2 of k2. + */ + Value valueResult = DistributedTestTools::Get(*delegateNew, KEY_1); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_1)); + valueResult = DistributedTestTools::Get(*delegateNew, KEY_2); + EXPECT_TRUE(DistributedTestTools::IsValueEquals(valueResult, VALUE_2)); + + statusClose = delegateManagerNew->CloseKvStore(delegateNew); + delegateNew = nullptr; + EXPECT_TRUE(statusClose == DBStatus::OK); + delete delegateManager; + delegateManager = nullptr; + delete delegateManagerNew; + delegateManagerNew = nullptr; +} + +/* + * @tc.name: ComplexAction 001 + * @tc.desc: Verify that can start transaction in new conn after close db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, ComplexAction001, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + /** + * @tc.steps: step1. open db then close it. + * @tc.expected: step1. close successfully. + */ + EXPECT_TRUE(g_transactionManager->CloseKvStore(g_transactionDelegate) == DBStatus::OK); + g_transactionDelegate = nullptr; + delete g_transactionManager; + g_transactionManager = nullptr; + + /** + * @tc.steps: step2. open db in new conn then start transaction and put (k1, v1) to db + * and then close the db without the transaction committed. + * @tc.expected: step2. start the db and transaction and close the db successfully. + */ + KvStoreDelegateManager *manager = nullptr; + KvOption option = g_kvOption; + option.createIfNecessary = false; + KvStoreDelegate *delegate = DistributedTestTools::GetDelegateSuccess(manager, g_kvdbParameter1, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(delegate->StartTransaction(), DBStatus::OK); + DBStatus status = DistributedTestTools::Put(*delegate, KEY_1, VALUE_1); + EXPECT_EQ(status, DBStatus::OK); + EXPECT_EQ(manager->CloseKvStore(delegate), DBStatus::OK); + delegate = nullptr; + delete manager; + manager = nullptr; + /** + * @tc.steps: step3. reopen the same db again and the data on db. + * @tc.expected: step3. can't find the data on db. + */ + g_transactionDelegate = DistributedTestTools::GetDelegateSuccess(g_transactionManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(g_transactionManager != nullptr && g_transactionDelegate != nullptr); + Value value = DistributedTestTools::Get(*g_transactionDelegate, KEY_1); + EXPECT_TRUE(value.size() == 0); +} + +/* + * @tc.name: ComplexAction 002 + * @tc.desc: Verify that can not start transaction after delete kv db. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, ComplexAction002, TestSize.Level1) +{ + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, + g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "fail to create exist kvdb"; + EXPECT_TRUE(delegateManager->CloseKvStore(delegate) == OK); + delegate = nullptr; + /** + * @tc.steps: step1. delete kv db. + * @tc.expected: step1. delete successfully. + */ + DBStatus statusDelete = delegateManager->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(statusDelete == DBStatus::OK); + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: ComplexAction 003 + * @tc.desc: Verify that can not delete kv db after start transaction. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, ComplexAction003, TestSize.Level1) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, g_kvdbParameter2_1_1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "fail to create exist kvdb"; + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + DBStatus statusStart = delegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + /** + * @tc.steps: step2. delete db. + * @tc.expected: step2. delete failed. + */ + DBStatus statusDelete = delegateManager->DeleteKvStore(STORE_ID_2); + EXPECT_TRUE(statusDelete != DBStatus::OK); + + DBStatus statusCommit = delegate->Commit(); + EXPECT_TRUE(statusCommit == DBStatus::OK); + DBStatus statusClose = delegateManager->CloseKvStore(delegate); + delegate = nullptr; + EXPECT_TRUE(statusClose == DBStatus::OK); + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: CommitHistory 001 + * @tc.desc: Verify that execute a transaction will generate a record. + * @tc.type: FUNC + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, CommitHistory001, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "fail to create exist kvdb"; + + vector entries1, entries1Up, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries1Up.push_back(ENTRY_1_2); + entries1Up.push_back(ENTRY_2_3); + entries2.push_back(ENTRY_3); + entries2.push_back(ENTRY_2_4); + /** + * @tc.steps: step1. start transaction and put(k1,v1) then commit. + * @tc.expected: step1. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(DistributedTestTools::Put(*delegate, KEY_1, VALUE_1) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step2. start transaction and update(k1,v1) to (k1,v2) then commit. + * @tc.expected: step2. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(DistributedTestTools::Put(*delegate, KEY_1, VALUE_2) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step3. start transaction and delete(k1) then commit. + * @tc.expected: step3. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(DistributedTestTools::Delete(*delegate, KEY_1) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step4. start transaction and putBatch(k1,v1)(k2,v2) then commit. + * @tc.expected: step4. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(delegate->PutBatch(entries1) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step5. start transaction and putBatch(k1,v2)(k2,v3) then commit. + * @tc.expected: step5. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(delegate->PutBatch(entries1Up) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step6. start transaction and deleteBatch(k1)(k2) putBatch (k2,v4)(k3,v3) then commit. + * @tc.expected: step6. operate successfully. + */ + EXPECT_TRUE(delegate->StartTransaction() == DBStatus::OK); + EXPECT_TRUE(delegate->DeleteBatch(KEYS_1) == DBStatus::OK); + EXPECT_TRUE(delegate->PutBatch(entries2) == DBStatus::OK); + EXPECT_TRUE(delegate->Commit() == DBStatus::OK); + /** + * @tc.steps: step7. check transaction committion records by hand. + * @tc.expected: step7. There are 6 records. + */ + MST_LOG("please check the committed transaction records..."); + std::this_thread::sleep_for(std::chrono::duration(WAIT_FOR_CHECKING_SECONDS)); + + EXPECT_TRUE(delegateManager->CloseKvStore(delegate) == DBStatus::OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: CommitHistory 003 + * @tc.desc: Verify that transaction record will be null if rollback transaction. + * @tc.type: Fault injection + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, CommitHistory003, TestSize.Level2) +{ + DistributedTestTools::Clear(*g_transactionDelegate); + + KvStoreDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedTestTools::GetDelegateSuccess(delegateManager, + g_kvdbParameter1, g_kvOption); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "fail to create exist kvdb"; + + /** + * @tc.steps: step1. start transaction and put(k1,v1) then rollback. + * @tc.expected: step1. operate successfully. + */ + DBStatus statusStart = g_transactionDelegate->StartTransaction(); + EXPECT_TRUE(statusStart == DBStatus::OK); + DBStatus statusPut = DistributedTestTools::Put(*g_transactionDelegate, KEY_1, VALUE_1); + EXPECT_TRUE(statusPut == DBStatus::OK); + DBStatus statusRollback = g_transactionDelegate->Rollback(); + EXPECT_TRUE(statusRollback == DBStatus::OK); + /** + * @tc.steps: step2. check submit records by hand. + * @tc.expected: step2. record is null. + */ + MST_LOG("please check the committed transaction records, and verify it is deleted..."); + std::this_thread::sleep_for(std::chrono::duration(WAIT_FOR_CHECKING_SECONDS)); + + delegateManager->CloseKvStore(delegate); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +/* + * @tc.name: BasicActionAcid 001 + * @tc.desc: Verify that transaction between put and putbatch. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid001, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and putBatch (k0-k127,1) then update v1=2 of k1. + * @tc.expected: step1. operate successfully and get v1=2 of k1 finally. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::PUT_BATCH, CrudMode::PUT, + false, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 002 + * @tc.desc: Verify that transaction between delete and deletebatch. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid002, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and deleteBatch (k0-k127,1) then delete k1. + * @tc.expected: step1. operate successfully and no data exist in db. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::DELETE_BATCH, + CrudMode::DELETE, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 003 + * @tc.desc: Verify that transaction between put and deletebatch. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid003, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and deleteBatch (k0-k127,1) then put (k1,v1=2). + * @tc.expected: step1. operate successfully and get v1=2 of k1. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::DELETE_BATCH, + CrudMode::PUT, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 004 + * @tc.desc: Verify that transaction between putbatch and delete. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid004, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and putBatch (k0-k127,1) then delete k1. + * @tc.expected: step1. operate successfully and get null of k1 but the size of keys is 128. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::PUT_BATCH, + CrudMode::DELETE, false, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 005 + * @tc.desc: Verify that transaction between put and clear. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid005, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and clear db then put (k1,v1=2). + * @tc.expected: step1. operate successfully and get v1=2 of k1. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::CLEAR, + CrudMode::PUT, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 006 + * @tc.desc: Verify that transaction between putbatch and clear. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid006, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and putBatch(k0-k127,values=2) then clear db. + * @tc.expected: step1. operate successfully and get null of db. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::PUT_BATCH, + CrudMode::CLEAR, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 007 + * @tc.desc: Verify that transaction between deletebatch and putbatch. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid007, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and deleteBatch(keys,values) then putBatch(k0-k127,v=2). + * @tc.expected: step1. operate successfully and the size getEntries is 128,values=2. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, + CrudMode::DELETE_BATCH, CrudMode::PUT_BATCH, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} + +/* + * @tc.name: BasicActionAcid 008 + * @tc.desc: Verify that transaction between deletebatch and clear. + * @tc.type: Pressure + * @tc.require: SR000BUH3J + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbKvTransactionTest, BasicActionAcid008, TestSize.Level2) +{ + /** + * @tc.steps: step1. start transaction and clear then deleteBatch(k0-k127,v=2). + * @tc.expected: step1. operate successfully and no data exist in db finally. + */ + for (int index = 0; index < BASIC_ACID_RUN_TIME; ++index) { + DistributedCrudTransactionTools transactionTools(*g_transactionDelegate, CrudMode::DELETE_BATCH, + CrudMode::CLEAR, true, IS_LOCAL); + EXPECT_TRUE(transactionTools.testCrudTransaction()); + } +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_backup_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_backup_test.cpp new file mode 100755 index 000000000..afb4a0337 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_backup_test.cpp @@ -0,0 +1,1457 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#include "types.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "process_communicator_test_stub.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbBackup { +KvStoreNbDelegate *g_nbBackupDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; +static std::condition_variable g_backupVar; + +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +DistributedDB::CipherPassword g_filePasswd1; +DistributedDB::CipherPassword g_filePasswd2; + +class DistributeddbNbBackupTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbBackupTest::SetUpTestCase(void) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + manager->SetProcessLabel("MST", "GetDevicesID"); + manager->SetProcessCommunicator(std::make_shared()); + delete manager; + manager = nullptr; + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + (void)g_filePasswd1.SetValue(FILE_PASSWD_VECTOR_1.data(), FILE_PASSWD_VECTOR_1.size()); + (void)g_filePasswd2.SetValue(FILE_PASSWD_VECTOR_2.data(), FILE_PASSWD_VECTOR_2.size()); +} + +void DistributeddbNbBackupTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbBackupTest::SetUp(void) +{ + const std::string exportPath = NB_DIRECTOR + "export"; + RemoveDatabaseDirectory(exportPath); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_nbBackupDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_nbBackupDelegate != nullptr); +} + +void DistributeddbNbBackupTest::TearDown(void) +{ + MST_LOG("TearDown after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_nbBackupDelegate, STORE_ID_1, g_option.isMemoryDb)); +} + +/* + * @tc.name: ExportTest001 + * @tc.desc: test checking in parameter of filepath of export interface. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. call export interface and the dir of filepath is nonexistent. + * @tc.expected: step1. call failed and return INVALID_ARGS. + */ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + const std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), INVALID_ARGS); + + /** + * @tc.steps: step2. call export interface, the filepath is absolute path and legal then check file number. + * @tc.expected: step2. call successfully and the file number is 1. + */ + SetDir(exportPath); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath); + RemoveDatabaseDirectory(exportPath); + + /** + * @tc.steps: step3. call export interface to export data and the dir of filepath is no r,w right. + * @tc.expected: step3. call failed return INVALID_ARGS. + */ +#ifdef RUNNING_ON_SIMULATED_ENV + const std::string noRightPath = "../noright"; + const int authRight = 0111; + SetDir(noRightPath, authRight); + std::string filePath2 = noRightPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, NULL_PASSWD), NO_PERMISSION); + RemoveDatabaseDirectory(noRightPath); +#endif + /** + * @tc.steps: step4. call export interface, the filepath is relative path and legal then check file number. + * @tc.expected: step4. call successfully and the file number is 1. + */ + const std::string exportPath2 = "../export"; + SetDir(exportPath2); + const std::string filePath3 = exportPath2 + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath3, NULL_PASSWD), OK); + CheckFileNumber(exportPath2, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath2); +} + +/* + * @tc.name: ExportTest 002 + * @tc.desc: Verify that the export interface will return FILE_ALREADY_EXISTED if the export file has been exist and + * the memory db doesn't support export. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. call export interface and the dir of filepath is legal then check file number. + * @tc.expected: step1. call successfully and the file number of filepath is 1. + */ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, NULL_PASSWD) == OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step2. call export interface and the directory of file is existent. + * @tc.expected: step2. call failed and return FILE_ALREADY_EXISTED. + */ + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, NULL_PASSWD) == FILE_ALREADY_EXISTED); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); // the number of files is 1. + + /** + * @tc.steps: step3. create memory db and call export interface. + * @tc.expected: step3. call failed and return NOT_SUPPORT. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option; + option.isMemoryDb = true; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + filePath = exportPath + "/bkpDB1.bin"; + EXPECT_TRUE(delegate->Export(filePath, NULL_PASSWD) == NOT_SUPPORT); + EXPECT_EQ((manager->CloseKvStore(delegate)), OK); + + /** + * @tc.steps: step4. call export interface when there is unclosed resultset and check the number of files. + * @tc.expected: step4. call successfully and the number of files is 2. + */ + KvStoreResultSet *resultSet; + EXPECT_EQ(g_nbBackupDelegate->GetEntries(KEY_EMPTY, resultSet), OK); + filePath = exportPath + "/bkpDB2.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, NULL_PASSWD) == OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the number of files is 2. + EXPECT_EQ(g_nbBackupDelegate->CloseResultSet(resultSet), OK); + RemoveDir(exportPath); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ExportTest 003 + * @tc.desc: Verify that the export interface will return busy when the import hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest003, TestSize.Level2) +{ + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, NULL_PASSWD) == OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully. + */ + thread subThread([&]() { + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, NULL_PASSWD), OK); + g_backupVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. return busy if step1 hasn't completed, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_TWO_HUNDREDS_MS)); + filePath = exportPath + "/bkpDB1.bin"; + DBStatus status = g_nbBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_TRUE(status == BUSY || status == OK); + CheckFileNumber(exportPath, fileCount); + if (status == BUSY) { + EXPECT_EQ(fileCount, 1); + } else { + EXPECT_EQ(fileCount, 2); // if export success, the number of file is 2. + } + + std::mutex count; + std::unique_lock lck(count); + g_backupVar.wait(lck); + RemoveDir(exportPath); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ExportTest 004 + * @tc.desc: Verify that the export interface will return busy when the rekey hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest004, TestSize.Level3) +{ + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, NB_PREDATA_NUM, ONE_K_LONG_STRING, TWO_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully if step1 running before step2, else return busy. + */ + thread subThread([&]() { + DBStatus rekeyStatus = g_nbBackupDelegate->Rekey(g_passwd1); + EXPECT_TRUE(rekeyStatus == OK || rekeyStatus == BUSY); + MST_LOG("The rekeyStatus is %d", rekeyStatus); + g_backupVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. return busy if step1 is running, else return OK. + */ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + const std::string filePath = exportPath + "/bkpDB.bin"; + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + DBStatus status = g_nbBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_TRUE(status == BUSY || status == OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_TRUE(fileCount == 0 || fileCount == 1); + + std::mutex count; + std::unique_lock lck(count); + g_backupVar.wait(lck); + RemoveDir(exportPath); +} +#endif + +/* + * @tc.name: ExportTest 005 + * @tc.desc: Verify that the export interface will execute blockly when last export hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest005, TestSize.Level2) +{ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, FOUR_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + /** + * @tc.steps: step1. call import interface to import the file that prepared in advance. + * @tc.expected: step1. call successfully. + */ + bool exportFlag = false; + thread subThread([&]() { + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + DBStatus exportStatus = g_nbBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(exportStatus, OK); + exportFlag = true; + g_backupVar.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call export interface when step1 is running. + * @tc.expected: step2. it can be called after last export finished. + */ + std::string filePath2 = exportPath + "/bkpDB1.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + + std::mutex count; + std::unique_lock lck(count); + g_backupVar.wait(lck, [&]{return exportFlag;}); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the export file number is 2. + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 006 + * @tc.desc: Verify that the put/delete operation will execute blockly when export hasn't completed. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest006, TestSize.Level3) +{ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, FIFTY_RECORDS, ONE_K_LONG_STRING, TWO_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + EXPECT_TRUE(g_nbBackupDelegate->PutLocal(iter->key, iter->value) == OK); + } + /** + * @tc.steps: step1. call export interface in subthread. + * @tc.expected: step1. call successfully. + */ + bool exportFlag = false; + thread subThread([&]() { + DBStatus exportStatus = g_nbBackupDelegate->Export(filePath, NULL_PASSWD); + EXPECT_EQ(exportStatus, OK); + exportFlag = true; + g_backupVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call Get,GetLocal,GetEntries to query data from db. + * @tc.expected: step2. call successfully(if export continued over 30s, put will return busy). + */ + Value valueResult; + EXPECT_TRUE(g_nbBackupDelegate->Get(allKeys[0], valueResult) == OK); + EXPECT_EQ(valueResult, entriesBatch[0].value); + EXPECT_TRUE(g_nbBackupDelegate->GetLocal(allKeys[1], valueResult) == OK); + EXPECT_EQ(valueResult, entriesBatch[1].value); + std::vector entries; + EXPECT_TRUE(g_nbBackupDelegate->GetEntries(KEY_EMPTY, entries) == OK); + EXPECT_EQ(entries.size(), entriesBatch.size()); + /** + * @tc.steps: step3. call put/delete/putlocal/deletelocal interface when step1 is running. + * @tc.expected: step3. return ok, if step1 need more than 30s to finish, it will return busy. + */ + DBStatus status = g_nbBackupDelegate->Put(entriesBatch[0].key, entriesBatch[0].value); + EXPECT_TRUE((status == OK) || (status == BUSY)); + status = g_nbBackupDelegate->PutLocal(entriesBatch[1].key, entriesBatch[1].value); + EXPECT_TRUE((status == OK) || (status == BUSY)); + status = g_nbBackupDelegate->Delete(allKeys[0]); + EXPECT_TRUE((status == OK) || (status == BUSY)); + + std::mutex count; + std::unique_lock lck(count); + g_backupVar.wait(lck, [&]{return exportFlag;}); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + RemoveDir(exportPath); +} + +/* + * @tc.name: ExportTest 007 + * @tc.desc: Verify that passwd parameter of export interface decide the file exported is encrypted or not. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ExportTest007, TestSize.Level1) +{ + int fileCount = 0; + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB1.bin"; + + /** + * @tc.steps: step1. call export interface with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step2. call export interface with the passwd = password(129B). + * @tc.expected: step2. return INVALID_ARGS. + */ + filePath = exportPath + "/bkpDB2.bin"; + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 1); + + /** + * @tc.steps: step3. call export interface with the passwd = password(1B). + * @tc.expected: step3. return OK. + */ + filePath = exportPath + "/bkpDB3.bin"; + passwordVector.assign(1, 'b'); // 1 Byte of passwd. + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, password), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 2); // the number of file is 2. + + /** + * @tc.steps: step3. call export interface with the passwd = password(128B). + * @tc.expected: step3. return OK. + */ + filePath = exportPath + "/bkpDB4.bin"; + passwordVector.assign(BATCH_RECORDS, 'b'); // 1 Byte of passwd. + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, password), OK); + CheckFileNumber(exportPath, fileCount); + EXPECT_EQ(fileCount, 3); // the number of file is 3. + RemoveDir(exportPath); +} + +/* + * @tc.name: ImportTest 001 + * @tc.desc: Verify that it can only import success when the pass word is rightly match(exist and rightly or not). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. device put (k1, v2) to DB and then update it to (k1, v1). + * @tc.expected: step1. put successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_1), OK); + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath1 = importPath + "/bkpDB1.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath1, NULL_PASSWD), OK); + std::string filePath2 = importPath + "/bkpDB2.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_2), OK); + + /** + * @tc.steps: step2. import backup file bkpDB1.bin with passwd f1. + * @tc.expected: step2. import failed and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), INVALID_FILE); + + /** + * @tc.steps: step3. import backup file bkpDB1.bin with empty passwd and get the value of key1. + * @tc.expected: step3. import successfully, and the value of key1 is value1. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), OK); + Value valueResult; + g_nbBackupDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(valueResult, VALUE_1); + + /** + * @tc.steps: step4. update the entry (k1, v1) to (k1, v2). + * @tc.expected: step4. update success. + */ + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_2), OK); + /** + * @tc.steps: step5. import backup file bkpDB2.bin with empty passwd. + * @tc.expected: step5. import failed and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), INVALID_FILE); + /** + * @tc.steps: step6. import backup file bkpDB2.bin with DB passwd p1. + * @tc.expected: step6. import failed and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step7. import backup file bkpDB2.bin with passwd = password(129B). + * @tc.expected: step7. import failed and return INVALID_FILE. + */ + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + /** + * @tc.steps: step8. import backup file bkpDB2.bin with DB passwd f1 and get the value of k1. + * @tc.expected: step8. import success and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), OK); + g_nbBackupDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(valueResult, VALUE_1); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 002 + * @tc.desc: Verify that memory DB can't support to import. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest002, TestSize.Level0) +{ + std::string importPath1 = NB_DIRECTOR + "export"; + SetDir(importPath1); + std::string filePath = importPath1 + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. create memory DB and import the backup file. + * @tc.expected: step1. create success but import failed and return NOT_SUPPORT. + */ + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + Option option; + option.isMemoryDb = true; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + EXPECT_EQ(delegate2->Import(filePath, NULL_PASSWD), NOT_SUPPORT); + /** + * @tc.steps: step2. the disk DB import backup file bkpDB1.bin from noexsit path with empty passwd. + * @tc.expected: step2. import failed and returned INVALID_FILE. + */ + std::string importPath2 = NB_DIRECTOR + "/noexsit"; + std::string filePath2 = importPath2 + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), INVALID_ARGS); + + /** + * @tc.steps: step3. the disk DB import noexsit backup file bkpDB1.bin from noexsit path with empty passwd.. + * @tc.expected: step3. import failed and returned INVALID_FILE. + */ + std::string filePath3 = importPath1 + "/bkpDB2.bin"; + EXPECT_EQ(g_nbBackupDelegate->Import(filePath3, NULL_PASSWD), INVALID_FILE); + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + delete manager2; + manager2 = nullptr; + RemoveDir(importPath1); +} + +/* + * @tc.name: ImportTest 003 + * @tc.desc: Verify that it can't import the not DB file and damaged file. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest003, TestSize.Level0) +{ + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. import a.txt in the right path. + * @tc.expected: step1. call failed and return INVALID_FILE. + */ + std::string filePath1 = importPath + "/a.txt"; + ofstream createFile(filePath1); + if (createFile) { + createFile << '1' << endl; + createFile.close(); + } + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + + /** + * @tc.steps: step2. write some data to DB file1. + * @tc.expected: step2. write successfully + */ + ofstream damageFile(filePath, ios::out | ios::binary); + ASSERT_TRUE(damageFile.is_open()); + damageFile.write(reinterpret_cast(&filePath), filePath.size()); + damageFile.close(); + + /** + * @tc.steps: step3. import DB file with empty password. + * @tc.expected: step3. import failed and returned INVALID_FILE + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, NULL_PASSWD), INVALID_FILE); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 004 + * @tc.desc: Verify that multi-DB can't import nb-DB backup file, and nb-DB can't import multi-DB backup file too. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest004, TestSize.Level1) +{ + KvStoreDelegate *multiEncryptDelegate = nullptr; + KvStoreDelegateManager *multiEncryptManager = nullptr; + + KvOption option = g_kvOption; + multiEncryptDelegate = DistributedTestTools::GetDelegateSuccess(multiEncryptManager, g_kvdbParameter2, option); + ASSERT_TRUE(multiEncryptManager != nullptr && multiEncryptDelegate != nullptr); + const std::string multiImportPath = DIRECTOR + "export"; + SetDir(multiImportPath); + std::string multiFilePath1 = multiImportPath + "/bkpDB1.bin"; + multiEncryptDelegate->Export(multiFilePath1, g_filePasswd1); + + KvStoreDelegate *localDelegate = nullptr; + KvStoreDelegateManager *localManager = nullptr; + option.localOnly = true; + localDelegate = DistributedTestTools::GetDelegateSuccess(localManager, g_kvdbParameter3, option); + ASSERT_TRUE(localManager != nullptr && localDelegate != nullptr); + + std::string multiFilePath2 = multiImportPath + "/bkpDB2.bin"; + localDelegate->Export(multiFilePath2, g_filePasswd1); + + const std::string nbImportPath = NB_DIRECTOR + "export"; + SetDir(nbImportPath); + std::string nbFilePath = nbImportPath + "/bkpDB1.bin"; + g_nbBackupDelegate->Export(nbFilePath, g_filePasswd1); + /** + * @tc.steps: step1. the multi DB1 import the nb DB backup file with the password f1. + * @tc.expected: step1. call failed and return INVALID_FILE. + */ + EXPECT_EQ(multiEncryptDelegate->Import(nbFilePath, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step2. the multi DB1 import backup file of local db with the password f1. + * @tc.expected: step2. call failed and return INVALID_FILE. + */ + EXPECT_EQ(multiEncryptDelegate->Import(multiFilePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step3. the nb DB import the multi DB backup file with the password f1. + * @tc.expected: step3. call failed and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(multiFilePath1, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step4. the nb DB import the noexsit nb DB backup file with the password f1. + * @tc.expected: step4. call failed and return INVALID_FILE. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(multiFilePath2, g_filePasswd1), INVALID_FILE); + + /** + * @tc.steps: step5. Local DB import nb-backup file with the password f1. + * @tc.expected: step5. call failed and return INVALID_FILE. + */ + EXPECT_EQ(localDelegate->Import(nbFilePath, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step6. Local DB import multi-backup file with the password f1. + * @tc.expected: step6. call failed and return INVALID_FILE. + */ + EXPECT_EQ(localDelegate->Import(multiFilePath1, g_filePasswd1), INVALID_FILE); + EXPECT_EQ(multiEncryptManager->CloseKvStore(multiEncryptDelegate), OK); + multiEncryptDelegate = nullptr; + EXPECT_EQ(multiEncryptManager->DeleteKvStore(STORE_ID_2), OK); + delete multiEncryptManager; + multiEncryptManager = nullptr; + EXPECT_EQ(localManager->CloseKvStore(localDelegate), OK); + localDelegate = nullptr; + EXPECT_EQ(localManager->DeleteKvStore(STORE_ID_3), OK); + delete localManager; + localManager = nullptr; + RemoveDir(multiImportPath); + RemoveDir(nbImportPath); +} + +/* + * @tc.name: ImportTest 006 + * @tc.desc: Verify that many kvstore of one DB or the DB registered observer can't import the backup file. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest006, TestSize.Level0) +{ + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, g_filePasswd1), OK); + + /** + * @tc.steps: step1. create another delegate of the same storeId. + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter1, g_option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + + /** + * @tc.steps: step2. use the second delegate to import the backup file. + * @tc.expected: step2. import failed and return busy + */ + EXPECT_EQ(delegate2->Import(filePath, g_filePasswd1), BUSY); + + EXPECT_TRUE(manager2->CloseKvStore(delegate2) == OK); + delegate2 = nullptr; + delete manager2; + manager2 = nullptr; + /** + * @tc.steps: step3. delegate1 register observer. + * @tc.expected: step3. register success + */ + KvStoreObserverImpl observerSync; + EXPECT_EQ(g_nbBackupDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observerSync), OK); + /** + * @tc.steps: step4. delegate1 import the backup file with the password f1. + * @tc.expected: step4. register success + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, g_filePasswd1), BUSY); + RemoveDir(importPath); +} + +/* + * @tc.name: ImportTest 007 + * @tc.desc: Verify that the DB registered conflict notifier or hadn't resultSet can't import the backup file. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest007, TestSize.Level0) +{ + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, g_filePasswd1), OK); + + /** + * @tc.steps: step1. register conflict notifier. + * @tc.expected: step1. register successfully. + */ + ConflictNbCallback callback; + std::vector conflictData; + auto notifier = bind(&ConflictNbCallback::NotifyCallBack, &callback, placeholders::_1, &conflictData); + EXPECT_EQ(g_nbBackupDelegate->SetConflictNotifier(CONFLICT_NATIVE_ALL, notifier), OK); + /** + * @tc.steps: step2. import the backup file with the password f1. + * @tc.expected: step2. import failed and returned BUSY. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, g_filePasswd1), BUSY); + EXPECT_EQ(g_nbBackupDelegate->SetConflictNotifier(CONFLICT_NATIVE_ALL, nullptr), OK); + + /** + * @tc.steps: step3. call GetEntries to get resultSet. + * @tc.expected: step3. get successfully. + */ + KvStoreResultSet *resultSet; + EXPECT_EQ(g_nbBackupDelegate->GetEntries(KEY_K, resultSet), OK); + /** + * @tc.steps: step4. import the backup file with the password f1. + * @tc.expected: step4. import failed and returned BUSY. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, g_filePasswd1), BUSY); + EXPECT_EQ(g_nbBackupDelegate->CloseResultSet(resultSet), OK); + + RemoveDir(importPath); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ImportTest 008 + * @tc.desc: Verify that it will return busy when GetKvStore again or crud during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest008, TestSize.Level3) +{ + vector entries; + std::vector allKeys; + GenerateFixedRecords(entries, allKeys, FIFTY_RECORDS, KEY_SIX_BYTE, TWO_M_LONG_STRING); + for (auto iter = entries.begin(); iter != entries.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start subthread to import the backup file with empty password. + * @tc.expected: step1. start successfully if step1 running before step2, else return BUSY. + */ + thread subThread([&]() { + DBStatus importStatus = g_nbBackupDelegate->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(importStatus == OK || importStatus == BUSY); + g_backupVar.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. GetKvStore again during the subthread is importing. + * @tc.expected: step2. GetKvStore failed and returned BUSY if step1 is running, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + KvStoreDelegateManager *manager = nullptr; + DBStatus statusReturn; + DistributedDBNbTestTools::GetNbDelegateStatus(manager, statusReturn, g_dbParameter1, g_option); + EXPECT_TRUE(statusReturn == BUSY || statusReturn == OK); + /** + * @tc.steps: step3. put (k1, v1) during the subthread is importing. + * @tc.expected: step3. put failed and returned BUSY if step1 is running, else return OK. + */ + statusReturn = g_nbBackupDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(statusReturn == BUSY || statusReturn == OK); + std::mutex rekeyMtx; + std::unique_lock lck(rekeyMtx); + g_backupVar.wait(lck); + RemoveDir(importPath); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ImportTest 009 + * @tc.desc: Verify that it will return busy when rekey during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest009, TestSize.Level3) +{ + vector entriesRekey; + std::vector allKeys; + GenerateFixedRecords(entriesRekey, allKeys, FIFTY_RECORDS, KEY_SIX_BYTE, FOUR_M_LONG_STRING); + for (auto iter = entriesRekey.begin(); iter != entriesRekey.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start subthread to import the backup file with empty password. + * @tc.expected: step1. start successfully if step1 running before step2, else return BUSY. + */ + thread subThread([&]() { + DBStatus importStatus = g_nbBackupDelegate->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(importStatus == OK || importStatus == BUSY); + g_backupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. rekey during the subthread is importing. + * @tc.expected: step2. rekey failed and returned BUSY if step1 is running, else return OK. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LAST_SYNC)); + DBStatus status = g_nbBackupDelegate->Rekey(g_passwd1); + EXPECT_TRUE(status == BUSY || status == OK); + + std::mutex rekeyMtx; + std::unique_lock lck(rekeyMtx); + g_backupVar.wait(lck); + RemoveDir(importPath); +} +#endif + +/* + * @tc.name: ImportTest 010 + * @tc.desc: Verify that it will return busy when rekey during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest010, TestSize.Level3) +{ + vector entriesRekey2; + std::vector allKeys; + GenerateFixedRecords(entriesRekey2, allKeys, FIFTY_RECORDS, KEY_SIX_BYTE, TWO_M_LONG_STRING); + for (auto iter = entriesRekey2.begin(); iter != entriesRekey2.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBackupDelegate->Export(filePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start subthread to rekey DB with password 1. + * @tc.expected: step1. start successfully. + */ + bool rekeyFlag = false; + thread subThread([&]() { + EXPECT_EQ(g_nbBackupDelegate->Rekey(g_passwd1), OK); + rekeyFlag = true; + g_backupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. import the backup file during the subthread is rekeying. + * @tc.expected: step2. import failed and returned BUSY. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LAST_SYNC)); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, NULL_PASSWD), BUSY); + + std::mutex rekeyMtx; + std::unique_lock lck(rekeyMtx); + g_backupVar.wait(lck, [&]{return rekeyFlag;}); + RemoveDir(importPath); +} +#endif + +/* + * @tc.name: ImportTest 011 + * @tc.desc: Verify that it will allow to import backup file again during it is importing. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest011, TestSize.Level3) +{ + vector entriesReImport; + std::vector allKeys; + GenerateFixedRecords(entriesReImport, allKeys, TEN_RECORDS, KEY_SIX_BYTE, FOUR_M_LONG_STRING); + for (auto iter = entriesReImport.begin(); iter != entriesReImport.end(); iter++) { + EXPECT_TRUE(g_nbBackupDelegate->Put(iter->key, iter->value) == OK); + } + + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + g_nbBackupDelegate->Export(filePath, NULL_PASSWD); + + /** + * @tc.steps: step1. start subthread to import the backup file. + * @tc.expected: step1. start successfully. + */ + bool importFlag = false; + thread subThread([&]() { + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, NULL_PASSWD), OK); + importFlag = true; + g_backupVar.notify_one(); + }); + subThread.detach(); + /** + * @tc.steps: step2. import the backup file during the subthread is importing with empty password. + * @tc.expected: step2. import successfully and return OK. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath, NULL_PASSWD), OK); + + std::mutex rekeyMtx; + std::unique_lock lck(rekeyMtx); + g_backupVar.wait(lck, [&]{return importFlag;}); + + RemoveDir(importPath); +} + +void NbSubImportThread(int index, std::string importPath) +{ + vector tenEntries; + std::vector allKeys; + GenerateFixedRecords(tenEntries, allKeys, FIVE_RECORDS, KEY_SIX_BYTE, FOUR_M_LONG_STRING); + /** + * @tc.steps: step1. every thread create one DB put 10 entries into the DB. + * @tc.expected: step1. put successfully. + */ + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + DBParameters parameter[] = {g_dbParameter2, g_dbParameter3, g_dbParameter4, g_dbParameter5, g_dbParameter6}; + Option option[] = {g_createDiskUnencrypted, g_createDiskUnencrypted, g_createDiskUnencrypted, + g_createDiskEncrypted, g_createDiskEncrypted}; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, parameter[index], option[index]); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + + for (auto iter = tenEntries.begin(); iter != tenEntries.end(); iter++) { + EXPECT_TRUE(delegate2->Put(iter->key, iter->value) == OK); + } + + /** + * @tc.steps: step2. every thread export DB to backup file. + * @tc.expected: step2. export successfully. + */ + DistributedDB::CipherPassword passwd[] = {NULL_PASSWD, NULL_PASSWD, NULL_PASSWD, g_filePasswd1, g_filePasswd2}; + + const std::string backupFile[] = {"/bkpDB1.bin", "/bkpDB2.bin", "/bkpDB3.bin", "/bkpDB4.bin", "/bkpDB5.bin"}; + std::string nbFilePath[] = {(importPath + backupFile[INDEX_ZEROTH]), (importPath + backupFile[INDEX_FIRST]), + (importPath + backupFile[INDEX_SECOND]), (importPath + backupFile[INDEX_THIRD]), + (importPath + backupFile[INDEX_FORTH])}; + delegate2->Export(nbFilePath[index], passwd[index]); + + /** + * @tc.steps: step3. every thread import backup file to DB. + * @tc.expected: step3. import successfully. + */ + EXPECT_EQ(delegate2->Import(nbFilePath[index], passwd[index]), OK); + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + std::string storeId[] = {STORE_ID_2, STORE_ID_3, STORE_ID_4, STORE_ID_5, STORE_ID_6}; + EXPECT_TRUE(manager2->DeleteKvStore(storeId[index]) == OK); + delete manager2; + manager2 = nullptr; +} + +/* + * @tc.name: ImportTest 012 + * @tc.desc: Verify that different DB export and import in the same time won't affect each other. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, ImportTest012, TestSize.Level3) +{ + std::vector threads; + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + for (int index = 0; index < FIVE_TIMES; ++index) { + threads.push_back(std::thread(NbSubImportThread, index, std::ref(importPath))); + } + + for (auto& th : threads) { + th.join(); + } + RemoveDir(importPath); +} + +/* + * @tc.name: Exchange 001 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, Exchange001, TestSize.Level1) +{ + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step3. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step4. rekey db1 with g_passwd1, and then import "bkpDB1.bin" and "bkpDB2.bin" with p1. + * @tc.expected: step4. rekey succeeded and import failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step5. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step5. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step6. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + RemoveDir(exportPath); +} + +/* + * @tc.name: Exchange 002 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, Exchange002, TestSize.Level1) +{ + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. rekey db1 with g_passwd1, + * export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd1. + * @tc.expected: step3. rekey succeeded and import failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step4. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step4. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step5. rekey db1 with NULL_PASSWD, + * import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step5. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_nbBackupDelegate->Rekey(NULL_PASSWD), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step6. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + RemoveDir(exportPath); +} + +/* + * @tc.name: Exchange 003 + * @tc.desc: whether current db is encrypted or not, import file need to use exported password(or NULL_PASSWD). + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, Exchange003, TestSize.Level1) +{ + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath1 = exportPath + "/bkpDB1.bin"; + std::string filePath2 = exportPath + "/bkpDB2.bin"; + /** + * @tc.steps: step1. rekey db1 with g_passwd1, + * export data as "bkpDB1.bin" with passwd g_filePasswd1 and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step1. call successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Rekey(g_passwd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Export(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step2. rekey db1 with g_passwd2, + * import "bkpDB1.bin" and "bkpDB2.bin" with NULL_PASSWD. + * @tc.expected: step2. import "bkpDB1.bin" failed and import "bkpDB2.bin" succeeded. + */ + EXPECT_EQ(g_nbBackupDelegate->Rekey(g_passwd2), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, NULL_PASSWD), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, NULL_PASSWD), OK); + /** + * @tc.steps: step3. Put (k1,v1), close db, open db with g_passwd2 and delete (k1,v1) + * @tc.expected: step3. operate successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ((g_manager->CloseKvStore(g_nbBackupDelegate)), OK); + g_nbBackupDelegate = nullptr; + delete g_manager; + g_manager = nullptr; + Option encrypted(true, false, true, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_2); + g_nbBackupDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, encrypted); + ASSERT_TRUE(g_manager != nullptr && g_nbBackupDelegate != nullptr); + EXPECT_EQ(g_nbBackupDelegate->Delete(KEY_1), OK); + /** + * @tc.steps: step4. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd1. + * @tc.expected: step4. import failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_passwd1), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_passwd1), INVALID_FILE); + /** + * @tc.steps: step5. import "bkpDB1.bin" and "bkpDB2.bin" with g_passwd2. + * @tc.expected: step5. import failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_passwd2), INVALID_FILE); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_passwd2), INVALID_FILE); + /** + * @tc.steps: step6. import "bkpDB1.bin" and "bkpDB2.bin" with g_filePasswd1. + * @tc.expected: step6. import "bkpDB1.bin" succeeded and import "bkpDB2.bin" failed. + */ + EXPECT_EQ(g_nbBackupDelegate->Import(filePath1, g_filePasswd1), OK); + EXPECT_EQ(g_nbBackupDelegate->Import(filePath2, g_filePasswd1), INVALID_FILE); + /** + * @tc.steps: step7. Put (k2,v2), close db, open db with g_passwd2 and delete (k2,v2) + * @tc.expected: step7. operate successfully. + */ + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_2, VALUE_2), OK); + EXPECT_EQ((g_manager->CloseKvStore(g_nbBackupDelegate)), OK); + g_nbBackupDelegate = nullptr; + delete g_manager; + g_manager = nullptr; + g_nbBackupDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, encrypted); + ASSERT_TRUE(g_manager != nullptr && g_nbBackupDelegate != nullptr); + EXPECT_EQ(g_nbBackupDelegate->Delete(KEY_2), OK); + RemoveDir(exportPath); +} + +/* + * @tc.name: CorruptionHandler 001 + * @tc.desc: Verify that the Corruption Handler can be set only one, if set many times, the last one is valid. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, CorruptionHandler001, TestSize.Level1) +{ + EXPECT_TRUE(g_nbBackupDelegate->Put(KEY_1, VALUE_1) == OK); + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, NULL_PASSWD) == OK); + EXPECT_TRUE(g_nbBackupDelegate->Delete(KEY_1) == OK); + + /** + * @tc.steps: step1. device A call SetKvStoreCorruptionHandler. + * @tc.expected: step1. call successfully. + */ + KvStoreNbCorruptInfo corruptInfo; + KvStoreNbCorruptInfo corruptInfoNew; + auto notifier = bind(&KvStoreNbCorruptInfo::CorruptCallBack, &corruptInfo, + placeholders::_1, placeholders::_2, placeholders::_3); + g_manager->SetKvStoreCorruptionHandler(notifier); + /** + * @tc.steps: step2. device A call SetKvStoreCorruptionHandler the second time. + * @tc.expected: step2. call successfully. + */ + CallBackParam pathResult = {filePath, true}; + auto notifierNew = bind(&KvStoreNbCorruptInfo::CorruptNewCallBack, &corruptInfoNew, + placeholders::_1, placeholders::_2, placeholders::_3, std::ref(g_manager), pathResult); + g_manager->SetKvStoreCorruptionHandler(notifierNew); + EXPECT_TRUE(pathResult.result); + /** + * @tc.steps: step3. make file corrupted and call GetKvStore to trigger callback function. + * @tc.expected: step3. call successfully. + */ + EXPECT_EQ((g_manager->CloseKvStore(g_nbBackupDelegate)), OK); + g_nbBackupDelegate = nullptr; + std::string dbFliePath = DistributedDBNbTestTools::GetKvNbStoreDirectory(g_dbParameter1); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath)); + DBStatus status; + KvStoreDelegateManager *manager = nullptr; + EXPECT_EQ(DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, g_option), nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + /** + * @tc.steps: step4. open db and Get(k1) to verify importing in callback is successful. + * @tc.expected: step4. Get(k1)=v1. + */ + g_nbBackupDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_nbBackupDelegate != nullptr); + Value valueResult; + EXPECT_TRUE(g_nbBackupDelegate->Get(KEY_1, valueResult) == OK); + EXPECT_EQ(valueResult, VALUE_1); + g_manager->SetKvStoreCorruptionHandler(nullptr); + RemoveDir(exportPath); +} + +/* + * @tc.name: CorruptionHandler 002 + * @tc.desc: Verify that can import db files and can't export in the Corruption Handler callback when db flies + * are corrupted in using. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, CorruptionHandler002, TestSize.Level2) +{ + EXPECT_TRUE(g_nbBackupDelegate->Put(KEY_1, VALUE_1) == OK); + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, g_filePasswd1) == OK); + + /** + * @tc.steps: step1. device A call SetKvStoreCorruptionHandler to register CorruptCallBackOfImport. + * @tc.expected: step1. call successfully. + */ + KvStoreNbCorruptInfo corruptInfo; + CallBackParam pathResult = {filePath, true}; + auto notifier = bind(&KvStoreNbCorruptInfo::CorruptCallBackOfImport, &corruptInfo, + placeholders::_1, placeholders::_2, placeholders::_3, g_nbBackupDelegate, pathResult); + g_manager->SetKvStoreCorruptionHandler(notifier); + EXPECT_TRUE(pathResult.result); + /** + * @tc.steps: step2. device A modify the db files to trigger the CorruptCallBackOfImport and check. + * @tc.expected: step2. operate successfully and import the recover db files in callback successfully. + */ + EXPECT_TRUE(g_nbBackupDelegate->Delete(KEY_1) == OK); + std::string dbFliePath = DistributedDBNbTestTools::GetKvNbStoreDirectory(g_dbParameter1); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath)); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath + "-wal")); + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_1), INVALID_PASSWD_OR_CORRUPTED_DB); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); // wait for callback complete for 1 second. + Value valueResult; + EXPECT_TRUE(g_nbBackupDelegate->Get(KEY_1, valueResult) == OK); + EXPECT_EQ(valueResult, VALUE_1); + + /** + * @tc.steps: step3. device A call SetKvStoreCorruptionHandler again to register CorruptCallBackOfExport. + * @tc.expected: step3. call successfully. + */ + filePath = exportPath + "/bkpDB1.bin"; + auto notifierNew = bind(&KvStoreNbCorruptInfo::CorruptCallBackOfExport, &corruptInfo, + placeholders::_1, placeholders::_2, placeholders::_3, g_nbBackupDelegate, pathResult); + g_manager->SetKvStoreCorruptionHandler(notifierNew); + EXPECT_TRUE(pathResult.result); + + /** + * @tc.steps: step4. device A modify the db files to trigger the CorruptCallBackOfExport and check. + * @tc.expected: step4. operate successfully and export the recover db data in callback failed. + */ + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath)); + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_1), INVALID_PASSWD_OR_CORRUPTED_DB); + g_manager->SetKvStoreCorruptionHandler(nullptr); + RemoveDir(exportPath); +} + +/* + * @tc.name: CorruptionHandler 003 + * @tc.desc: Verify that if call SetKvStoreCorruptionHandler(nullptr) will unregister the Corruption Handler. + * @tc.type: FUNC + * @tc.require: SR000D4878 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBackupTest, CorruptionHandler003, TestSize.Level2) +{ + EXPECT_TRUE(g_nbBackupDelegate->Put(KEY_1, VALUE_1) == OK); + const std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbBackupDelegate->Export(filePath, g_filePasswd1) == OK); + + /** + * @tc.steps: step1. device A's db1 call SetKvStoreCorruptionHandler to register CorruptCallBack. + * @tc.expected: step1. call successfully. + */ + KvStoreNbCorruptInfo corruptInfo; + auto notifier = bind(&KvStoreNbCorruptInfo::CorruptCallBack, &corruptInfo, + placeholders::_1, placeholders::_2, placeholders::_3); + g_manager->SetKvStoreCorruptionHandler(notifier); + + /** + * @tc.steps: step2. device A's db2 call SetKvStoreCorruptionHandler to register CorruptCallBackOfImport. + * @tc.expected: step2. call successfully. + */ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *delegate = nullptr; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + KvStoreNbCorruptInfo corruptInfoNew; + CallBackParam pathResult = {filePath, true}; + auto notifierNew = bind(&KvStoreNbCorruptInfo::CorruptCallBackOfImport, &corruptInfoNew, + placeholders::_1, placeholders::_2, placeholders::_3, g_nbBackupDelegate, pathResult); + manager->SetKvStoreCorruptionHandler(notifierNew); + EXPECT_TRUE(pathResult.result); + + /** + * @tc.steps: step3. device A modify the db files to trigger the CorruptCallBackOfImport and check. + * @tc.expected: step3. operate successfully and import the recover db files in callback successfully. + */ + std::string dbFliePath = DistributedDBNbTestTools::GetKvNbStoreDirectory(g_dbParameter1); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath)); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath + "-wal")); + EXPECT_EQ(g_nbBackupDelegate->Put(KEY_1, VALUE_1), INVALID_PASSWD_OR_CORRUPTED_DB); + std::this_thread::sleep_for(std::chrono::seconds(TWO_SECONDS)); // wait for callback complete for 1 second. + Value valueResult; + EXPECT_EQ(g_nbBackupDelegate->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + + /** + * @tc.steps: step4. device A call SetKvStoreCorruptionHandler again to register nullptr. + * @tc.expected: step4. call successfully. + */ + manager->SetKvStoreCorruptionHandler(nullptr); + + /** + * @tc.steps: step5. device A modify the db files to trigger the CorruptCallBack and check. + * @tc.expected: step5. put return error but trigger the CorruptCallBack failed. + */ + EXPECT_TRUE(delegate->Put(KEY_1, VALUE_1) == OK); + dbFliePath = DistributedDBNbTestTools::GetKvNbStoreDirectory(g_dbParameter2); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath)); + EXPECT_TRUE(DistributedDBNbTestTools::ModifyDatabaseFile(dbFliePath + "-wal")); + EXPECT_EQ(delegate->Put(KEY_1, VALUE_1), INVALID_PASSWD_OR_CORRUPTED_DB); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); // wait for callback complete for 1 second. + EXPECT_EQ(delegate->Get(KEY_1, valueResult), INVALID_PASSWD_OR_CORRUPTED_DB); + + EXPECT_EQ((manager->CloseKvStore(delegate)), OK); + delegate = nullptr; + manager->DeleteKvStore(STORE_ID_2); + delete manager; + manager = nullptr; + RemoveDir(exportPath); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_batch_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_batch_crud_test.cpp new file mode 100755 index 000000000..0d1662649 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_batch_crud_test.cpp @@ -0,0 +1,2472 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_nb_test_tools.h" +#include "process_communicator_test_stub.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbBatchCrud { +static std::condition_variable g_conditionNbBatchVar; +const int TWENTY_RECORDS = 20; +const int TWENTYFIVE_RECORDS = 25; +const int OBSERVER_COUNT = 8; +enum BatchTag { + PUTBATCH = 0, + DELETEBATCH = 1, + GETENTRIES = 2, +}; +KvStoreNbDelegate *g_nbBatchCrudDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; + +DistributedDB::CipherPassword g_passwd1; + +class DistributeddbNbBatchCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbBatchCrudTest::SetUpTestCase(void) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + manager->SetProcessLabel("MST", "GetDevicesID"); + manager->SetProcessCommunicator(std::make_shared()); + delete manager; + manager = nullptr; + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); +} + +void DistributeddbNbBatchCrudTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbBatchCrudTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_nbBatchCrudDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_nbBatchCrudDelegate != nullptr); +} + +void DistributeddbNbBatchCrudTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_nbBatchCrudDelegate, STORE_ID_1, g_option.isMemoryDb)); + RemoveDir(NB_DIRECTOR); +} + +/** + * @tc.name: SimpleData 001 + * @tc.desc: Verify that single-ver db can support putbatch to db file. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData001, TestSize.Level0) +{ + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_3); + entries2.push_back(ENTRY_4); + + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (k2, v2) to DB and check the data in DB. + * @tc.expected: step1. PutBatch successfully and the (k1, v1), (k2, v2) is in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + /** + * @tc.steps: step2. call PutBatch interface to Put (k3, v3), (k4, v4) to DB and check the data in DB. + * @tc.expected: step2. PutBatch successfully and (k3, v3), (k4, v4) is in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_3, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_3); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_4, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_4); +} + +/** + * @tc.name: SimpleData 002 + * @tc.desc: Verify that single-ver db can support update entries in batches. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData002, TestSize.Level0) +{ + vector entries1, entries2, entriesGot; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_2_3); + + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (k2, v2) to DB and check the data in DB. + * @tc.expected: step1. PutBatch successfully and can find (k1, v1), (k2, v2) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step2. call PutBatch interface to update (k1, v1), (k2, v2) to (k1, v2), (k2, v3) + * and check the data in DB. + * @tc.expected: step2. PutBatch successfully and the value of k1 is v2, the value of k2 is v3. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_3); +} + +/** + * @tc.name: SimpleData 003 + * @tc.desc: Verify that single-ver db can support DeleteBatch from db file. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData003, TestSize.Level0) +{ + vector entries, entryResults; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + entries.push_back(ENTRY_3); + entries.push_back(ENTRY_4); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + keys.push_back(KEY_3); + keys.push_back(KEY_4); + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (k2, v2), (k3, v3), (k4, v4) to DB + * and check the data in DB. + * @tc.expected: step1. PutBatch successfully and can find (k1, v1), (k2, v2), (k3, v3), (k4, v4) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_TRUE(CompareEntriesVector(entryResults, entries)); + + /** + * @tc.steps: step2. call DeleteBatch interface to delete (k1, v1), (k2, v2), (k3, v3), (k4, v4) from DB + * and check the data in DB. + * @tc.expected: step2. DeleteBatch successfully and the DB is empty. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 004 + * @tc.desc: Verify that single-ver db can putbatch value of which is null, but can't putBatch + * value of which is bigger than 4M. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData004, TestSize.Level0) +{ + vector entries1, entries2, entryResults; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2_NULL); + entries2.push_back(ENTRY_3); + DistributedDB::Entry entry; + entry.key = KEY_4; + entry.value.assign(FOUR_M_LONG_STRING + 1, 'v'); // the size of value is 4M + 1 + entries2.push_back(entry); + + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (k2, NULL) to DB and check the data in DB. + * @tc.expected: step1. PutBatch successfully and the value of k1 is v1, and the value of k2 is null. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_EMPTY); + + /** + * @tc.steps: step2. call PutBatch interface to Put (k3, v3), entry value of which is supper bigger than 4M + * and check the data in DB. + * @tc.expected: step2. PutBatch failed and returned INVALID_ARGS. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_EQ(entryResults.size(), entries1.size()); +} + +/** + * @tc.name: SimpleData 005 + * @tc.desc: Verify that single-ver db can't putbatch key of which is null bigger than 1k. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData005, TestSize.Level0) +{ + vector entries1, entries2, entryResults; + DistributedDB::Entry entry1, entry2; + entries1.push_back(ENTRY_1); + entry1.key = KEY_EMPTY; + entry1.value = VALUE_2; + entries1.push_back(entry1); + entries2.push_back(ENTRY_3); + entry2.key.assign(ONE_K_LONG_STRING + 1, 'k'); // the size of key is 1k + 1 + entry2.value = VALUE_4; + entries2.push_back(entry2); + + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (NULL, v2) to DB and check the data in DB. + * @tc.expected: step1. PutBatch failed and return INVALID_ARGS. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), INVALID_ARGS); + + /** + * @tc.steps: step2. call PutBatch interface to Put (k3, v3), entry value of which is supper bigger than 4M + * and check the data in DB. + * @tc.expected: step2. PutBatch failed and returned INVALID_ARGS. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 006 + * @tc.desc: Verify that single-ver db can't putbatch key of which is null bigger than 1k. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData006, TestSize.Level0) +{ + vector entries, entryResults; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + entries.push_back(ENTRY_3); + entries.push_back(ENTRY_4); + + vector keys1, keys2; + keys1.push_back(KEY_1); + DistributedDB::Key supperKey; + supperKey.assign(ONE_K_LONG_STRING + 1, 'k'); // the size of key is 1k + 1 + keys1.push_back(supperKey); + keys2.push_back(KEY_2); + keys2.push_back(KEY_EMPTY); + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2)(k3,v3)(k4,v4) to db. + * @tc.expected: step1. PutBatch successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_TRUE(CompareEntriesVector(entryResults, entries)); + + /** + * @tc.steps: step2. call DeleteBatch interface to delete k1, supperKey from DB and check the data in DB. + * @tc.expected: step2. DeleteBatch failed and the data in DB is not changed. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys1), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_EQ(entryResults.size(), entries.size()); + /** + * @tc.steps: step3. call DeleteBatch interface to delete k2 which is KEY_EMPTY from DB and check the data in DB. + * @tc.expected: step3. DeleteBatch failed and the data in DB is not changed. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys2), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_EQ(entryResults.size(), entries.size()); +} + +/** + * @tc.name: SimpleData 007 + * @tc.desc: Verify that single-ver db deletebatch the data that are noexist party will return OK, but will + * return NOT_FOUND when the keys are all noexist. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData007, TestSize.Level0) +{ + vector entries, entryResults; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + keys.push_back(KEY_3); + + /** + * @tc.steps: step1. call PutBatch interface to Put (k1, v1), (k2, v2) to DB + * and check the data in DB. + * @tc.expected: step1. PutBatch successfully and can find (k1, v1), (k2, v2) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_TRUE(CompareEntriesVector(entryResults, entries)); + + /** + * @tc.steps: step2. call DeleteBatch interface to delete (k1,k2,k3) from DB + * and check the data in DB. + * @tc.expected: step2. DeleteBatch successfully and the DB is empty. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); + + /** + * @tc.steps: step3. call DeleteBatch interface to delete (k1,k2,k3) from DB + * and check the data in DB. + * @tc.expected: step3. DeleteBatch return OK and the DB is empty. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 008 + * @tc.desc: Verify that when the items of batch operation over 128, the interface will return INVALID_ARGS. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData008, TestSize.Level0) +{ + vector entriesBatch, entryResults; + vector allKeys; + GenerateRecords(BATCH_RECORDS + 1, DEFAULT_START, allKeys, entriesBatch); + + /** + * @tc.steps: step1. PutBatch 129 items to DB and GetEntries(). + * @tc.expected: step1. PutBatch failed and GetEntries return NOT_FOUND. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->PutBatch(entriesBatch), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); + + /** + * @tc.steps: step2. DeleteBatch 129 items to DB and GetEntries(). + * @tc.expected: step2. DeleteBatch failed and GetEntries return NOT_FOUND. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->DeleteBatch(allKeys), INVALID_ARGS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 009 + * @tc.desc: Verify that rekey will return busy when do batch operation. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData009, TestSize.Level2) +{ + vector entriesBatch, entryResults; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch), OK); + + /** + * @tc.steps: step1. PutBatch to db. + * @tc.expected: step1. PutBatch successfully. + */ + bool subPutBatchFlag = false; + thread subThread([&]() { + DBStatus status = g_nbBatchCrudDelegate->PutBatch(entriesBatch); + EXPECT_TRUE(status == OK || status == BUSY); + subPutBatchFlag = true; + g_conditionNbBatchVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call Rekey when step1 is running. + * @tc.expected: step2. rekey return BUSY. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + DBStatus rekeyStatus = g_nbBatchCrudDelegate->Rekey(g_passwd1); + EXPECT_TRUE(rekeyStatus == OK || rekeyStatus == BUSY); + + /** + * @tc.steps: step3. GetEntries() from db. + * @tc.expected: step3. GetEntries the data that step1 putbatch. + */ + std::mutex count; + std::unique_lock lck(count); + g_conditionNbBatchVar.wait(lck, [&]{return subPutBatchFlag;}); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_EQ(entryResults.size(), entriesBatch.size()); +} + +/** + * @tc.name: SimpleData 010 + * @tc.desc: Verify that import will return busy when do batch operation. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, SimpleData010, TestSize.Level2) +{ + const std::string importPath = NB_DIRECTOR + "export"; + SetDir(importPath); + std::string filePath = importPath + "/bkpDB.bin"; + EXPECT_EQ(g_nbBatchCrudDelegate->Export(filePath, NULL_PASSWD), OK); + vector entriesBatch, entryResults; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, NB_PREDATA_NUM, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch), OK); + + /** + * @tc.steps: step1. DeleteBatch data. + * @tc.expected: step1. operation successfully. + */ + bool subDeleteBatchFlag = false; + thread subThread([&]() { + DBStatus deleteStatus = g_nbBatchCrudDelegate->DeleteBatch(allKeys); + EXPECT_TRUE(deleteStatus == OK || deleteStatus == BUSY); + subDeleteBatchFlag = true; + g_conditionNbBatchVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. call import interface when step1 is running. + * @tc.expected: step2. import return BUSY. + */ + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); + DBStatus importStatus = g_nbBatchCrudDelegate->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(importStatus == OK || importStatus == BUSY); + + /** + * @tc.steps: step3. GetEntries() from db. + * @tc.expected: step3. GetEntries return NOT_FOUND. + */ + std::mutex count; + std::unique_lock lck(count); + g_conditionNbBatchVar.wait(lck, [&]{return subDeleteBatchFlag;}); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), NOT_FOUND); +} + +void TestBatchComplexAction(vector &entries1, vector &entries12) +{ + /** + * @tc.steps: step3. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step3. PutBatch successfully get null of k1,k2. + */ + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1); + EXPECT_EQ(status, OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + /** + * @tc.steps: step4. PutBatch (k1,v2)(k2,v3) and then get. + * @tc.expected: step4. PutBatch successfully get v2 of k1,v3 of k2. + */ + status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries12); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_3); +} + +/** + * @tc.name: ComplexData 001 + * @tc.desc: Verify that single-ver db can support batch CRUD for the same keys continuously. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, ComplexData001, TestSize.Level0) +{ + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries12; + entries12.push_back(ENTRY_1_2); + entries12.push_back(ENTRY_2_3); + + vector keys1; + keys1.push_back(ENTRY_1.key); + keys1.push_back(ENTRY_2.key); + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1); + EXPECT_EQ(status, OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + /** + * @tc.steps: step2. DeleteBatch (k1)(k2) and then get. + * @tc.expected: step2. DeleteBatch successfully get return NOT_FOUND. + */ + status = DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys1); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), NOT_FOUND); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), NOT_FOUND); + + TestBatchComplexAction(entries1, entries12); + + /** + * @tc.steps: step5. DeleteBatch (k1)(k2) and then get. + * @tc.expected: step5. DeleteBatch successfully get return NOT_FOUND. + */ + status = DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys1); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), NOT_FOUND); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), NOT_FOUND); +} + +/** + * @tc.name: ComplexData 002 + * @tc.desc: Verify that single-ver db can support batch CRUD for the different keys continuously. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, ComplexData002, TestSize.Level2) +{ + vector entriesBatch, entriesRes; + vector allKeys; + /** + * @tc.steps: step1-2. PutBatch 100 items of (keys,values) then query and deletebatch for 100 times. + * @tc.expected: step1-2. operate successfully. + */ + GenerateFixedLenRandRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, KEY_SIX_BYTE, KEY_ONE_HUNDRED_BYTE); + for (int count = 0; count < ONE_HUNDRED_RECORDS; count++) { + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesRes), OK); + EXPECT_EQ(entriesRes.size(), entriesBatch.size()); + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, allKeys), OK); + entriesRes.clear(); + } + + /** + * @tc.steps: step3. PutBatch 20 items of (keys,values) and then query. + * @tc.expected: step3. PutBatch successfully GetEntries is right. + */ + entriesBatch.clear(); + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys, entriesBatch); + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesRes), OK); + EXPECT_EQ(entriesRes.size(), entriesBatch.size()); +} + +void CheckBatchCrud(vector entries, vector keys) +{ + /** + * @tc.steps: step3. Put(k1,v3) Put(k2,v4) Put(k3,v3) and then get. + * @tc.expected: step3. Put successfully get v3 of k1,v4 of k2,v3 of k3. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, ENTRY_1_3.key, ENTRY_1_3.value), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, ENTRY_2_4.key, ENTRY_2_4.value), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, ENTRY_3.key, ENTRY_3.value), OK); + vector entriesRes; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesRes), OK); + EXPECT_TRUE(CompareEntriesVector(entriesRes, entries)); + /** + * @tc.steps: step4. DeleteBatch (k2)(k3) and then get. + * @tc.expected: step4. DeleteBatch successfully get v3 of k1,get return NOT_FOUND of k2,k3. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_3); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), NOT_FOUND); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_3, valueResult), NOT_FOUND); + /** + * @tc.steps: step5. PutBatch(k1,v2)(k2,v3)(k3,v4) then get. + * @tc.expected: step5. Putbatch successfully, get v2 of k1, v3 of k2, v4 of k3. + */ + vector entries123; + entries123.push_back(ENTRY_1_2); + entries123.push_back(ENTRY_2_3); + entries123.push_back(ENTRY_3_4); + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries123); + EXPECT_EQ(status, OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_3); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_3, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_4); +} + +/** + * @tc.name: ComplexData 003 + * @tc.desc: Verify that can batch and single crud continuously. + * @tc.type: FUN + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, ComplexData003, TestSize.Level0) +{ + vector entries1; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + vector entries123; + entries123.push_back(ENTRY_1_3); + entries123.push_back(ENTRY_2_4); + entries123.push_back(ENTRY_3); + vector keys23; + keys23.push_back(ENTRY_2.key); + keys23.push_back(ENTRY_3.key); + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1); + EXPECT_EQ(status, OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + /** + * @tc.steps: step2. delete k1 and then get. + * @tc.expected: step2. delete successfully get return NOT_FOUND of k1, get v2 of k2. + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, valueResult), NOT_FOUND); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + CheckBatchCrud(entries123, keys23); +} + +void NbBatchCRUDThread(BatchTag tag) +{ + vector entriesBatch; + vector allKeys; + GenerateFixedLenRandRecords(entriesBatch, allKeys, TWENTY_RECORDS, + KEY_SIX_BYTE, KEY_ONE_HUNDRED_BYTE); + if (tag == PUTBATCH) { + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch); + EXPECT_EQ(status, OK); + } else if (tag == DELETEBATCH) { + DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, allKeys); + } else if (tag == GETENTRIES) { + vector entryResults; + DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, {'k'}, entryResults); + } +} + +/** + * @tc.name: ComplexData 005 + * @tc.desc: test that it won't be crash when there are 20 threads to batch operation. + * @tc.type: Pressure + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, ComplexData005, TestSize.Level2) +{ + /** + * @tc.steps: step1. start 20 threads to do batch operation. + * @tc.expected: step1. process is normal and no crash. + */ + std::vector threads; + for (int threadId = 0; threadId < TWENTY_RECORDS; ++threadId) { + int indexOption = GetRandInt(MODE_RAND_MIN, MODE_RAND_MAX); + threads.push_back(std::thread(NbBatchCRUDThread, BatchTag(indexOption))); + } + for (auto& th : threads) { + th.join(); + } + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); +} + +/** + * @tc.name: Observer 001 + * @tc.desc: Verify that putbatch will trigger callback after register observer for specific key. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Observer001, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_2_3); + entries2.push_back(ENTRY_3); + vector observerCallbackEntries; + + /** + * @tc.steps: step1. register observer1,observer2,observer3 for k1,k2,k3 with + * mode = OBSERVER_CHANGES_NATIVE. + * @tc.expected: step1. register successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_NATIVE, &observer3), OK); + + /** + * @tc.steps: step2. putbatch (k1,v1)(k2,v2) to db and check observer callback. + * @tc.expected: step2. putbatch successfully and the callback of observer1 and observer2 + * are all once INSERT_LIST. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + observerCallbackEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); + observer1.Clear(); + observer2.Clear(); + observer3.Clear(); + + /** + * @tc.steps: step3. putbatch (k2,v3)(k3,v3) to db and check observer callback. + * @tc.expected: step3. putbatch successfully and the callback of observer2 is once UPDATE_LIST, + * the callback of observer3 is once INSERT_LIST, there is no callback of observer1. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + observerCallbackEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, ListType::UPDATE_LIST, observerCallbackEntries)); + observerCallbackEntries.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::UPDATE_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); +} + +/** + * @tc.name: Observer 002 + * @tc.desc: Verify that deletebatch will trigger callback after register observer for specific key. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Observer002, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + vector entries1, entryResults; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + std::vector keys; + keys.push_back(KEY_3); + keys.push_back(KEY_2); + keys.push_back(KEY_1); + vector observerCallbackEntries; + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_TRUE(CompareEntriesVector(entryResults, entries1)); + + /** + * @tc.steps: step2. register observer1,observer2,observer3 for k1,k2,k3 with + * mode = OBSERVER_CHANGES_NATIVE. + * @tc.expected: step2. register successfully. + */ + DBStatus status = g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1); + EXPECT_EQ(status, OK); + status = g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2); + EXPECT_EQ(status, OK); + status = g_nbBatchCrudDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_NATIVE, &observer3); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step3. deletebatch (k1,k2,k3) and check observer callback. + * @tc.expected: step3. deletebatch successfully and the callback of observer1 and observer2 + * are all once DELETE_LIST, the observer3 won't receive callback. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + observerCallbackEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, ListType::DELETE_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::DELETE_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ZERO_TIME, ListType::DELETE_LIST, observerCallbackEntries)); +} + +/** + * @tc.name: Observer 003 + * @tc.desc: Verify that putbatch will trigger callback after register observer for specific key. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Observer003, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_1_2); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_2_3); + entries2.push_back(ENTRY_2_4); + entries2.push_back(ENTRY_3); + vector observerCallbackEntries; + + /** + * @tc.steps: step1. register observer1,observer2,observer3 for k1,k2,k3 with + * mode = OBSERVER_CHANGES_NATIVE. + * @tc.expected: step1. register successfully. + */ + DBStatus status = g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1); + EXPECT_EQ(status, OK); + status = g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2); + EXPECT_EQ(status, OK); + status = g_nbBatchCrudDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_NATIVE, &observer3); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. putbatch (k1,v1)(k2,v2) to db and check observer callback. + * @tc.expected: step2. putbatch successfully and the callback of observer1 and observer2 + * are all once INSERT_LIST. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + observerCallbackEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); + observer1.Clear(); + observer2.Clear(); + observer3.Clear(); + + /** + * @tc.steps: step3. putbatch (k2,v3)(k3,v3) to db and check observer callback. + * @tc.expected: step3. putbatch successfully and the callback of observer2 is once UPDATE_LIST, + * the callback of observer3 is once INSERT_LIST, there is no callback of observer1. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + observerCallbackEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, ListType::UPDATE_LIST, observerCallbackEntries)); + observerCallbackEntries.push_back(ENTRY_2_4); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::UPDATE_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ONE_TIME, ListType::INSERT_LIST, observerCallbackEntries)); +} + +/** + * @tc.name: Observer 004 + * @tc.desc: Verify that deletebatch will trigger callback after register observer for specific key. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Observer004, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2; + vector entries1, entryResults; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + std::vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + keys.push_back(KEY_1); + vector observerCallbackEntries; + + /** + * @tc.steps: step1. PutBatch (k1,v1)(k2,v2) and then get. + * @tc.expected: step1. PutBatch successfully get v1 of k1,v2 of k2. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entryResults), OK); + EXPECT_TRUE(CompareEntriesVector(entryResults, entries1)); + + /** + * @tc.steps: step2. register observer1,observer2 for k1, k2 with mode = OBSERVER_CHANGES_NATIVE. + * @tc.expected: step2. register successfully. + */ + DBStatus status = g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1); + EXPECT_EQ(status, OK); + status = g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step3. deletebatch (k1,k2,k1) and check observer callback. + * @tc.expected: step3. deletebatch successfully and observer1 and observer2 are both triggered once. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + observerCallbackEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, ListType::DELETE_LIST, observerCallbackEntries)); + observerCallbackEntries.clear(); + observerCallbackEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, ListType::DELETE_LIST, observerCallbackEntries)); +} + +/** + * @tc.name: Observer 005 + * @tc.desc: Verify that deletebatch will trigger once callback after register observer for all key. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Observer005, TestSize.Level0) +{ + vector entriesBatch; + vector allKeys; + GenerateRecords(TEN_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + /** + * @tc.steps: step1. register 8 observers for all key. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observerNative[OBSERVER_COUNT]; + for (int obsCnt = 0; obsCnt < OBSERVER_COUNT; obsCnt++) { + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, + &observerNative[obsCnt]), OK); + } + + /** + * @tc.steps: step2. putbatch 10 items of to db and check observer callback. + * @tc.expected: step2. putbatch successfully and the callback of all observers are once + * INSERT_LIST with 10 items data. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch), OK); + for (int index = 0; index < OBSERVER_COUNT; index++) { + EXPECT_TRUE(VerifyObserverResult(observerNative[index], CHANGED_ONE_TIME, ListType::INSERT_LIST, entriesBatch)); + } + for (int index = 0; index < OBSERVER_COUNT; index++) { + observerNative[index].Clear(); + } + + /** + * @tc.steps: step3. putbatch 10 items of to db and check observer callback. + * @tc.expected: step3. putbatch successfully and the callback of all observers are once + * UPDATE_LIST with 10 items data. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entriesBatch), OK); + for (int index = 0; index < OBSERVER_COUNT; index++) { + EXPECT_TRUE(VerifyObserverResult(observerNative[index], CHANGED_ONE_TIME, ListType::UPDATE_LIST, entriesBatch)); + } + for (int index = 0; index < OBSERVER_COUNT; index++) { + observerNative[index].Clear(); + } + + /** + * @tc.steps: step4. deletebatch 10 items of from db and check observer callback. + * @tc.expected: step4. putbatch successfully and the callback of all observers are once + * DELETE_LIST with 10 items data. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, allKeys), OK); + for (int index = 0; index < OBSERVER_COUNT; index++) { + EXPECT_TRUE(VerifyObserverResult(observerNative[index], CHANGED_ONE_TIME, ListType::DELETE_LIST, entriesBatch)); + } +} + +/** + * @tc.name: Transaction 001 + * @tc.desc: start transaction success and can only native DB support to put and local DB not. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction001, TestSize.Level0) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + /** + * @tc.steps: step2. use Put interface to put (k1, v1) and Get the value of k1; + * @tc.expected: step2. put successfully but can't find data in db + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + /** + * @tc.steps: step3. use PutLocal interface to put (k2, v2) and Get the value of k2; + * @tc.expected: step3. PutLocal failed and returned NOT_SUPPORT and can't find data in db + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbBatchCrudDelegate, KEY_2, VALUE_2), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetLocal(*g_nbBatchCrudDelegate, KEY_2, value), OK); + EXPECT_EQ(value, VALUE_2); + + /** + * @tc.steps: step4. commit the transaction and check the data in db again. + * @tc.expected: step4. commit successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::GetLocal(*g_nbBatchCrudDelegate, KEY_2, value), OK); + EXPECT_EQ(value, VALUE_2); +} + +/** + * @tc.name: Transaction 002 + * @tc.desc: verify that the db support Rollback but can't Rollback repeatedly. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction002, TestSize.Level0) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, value), OK); + EXPECT_EQ(value, VALUE_2); + + /** + * @tc.steps: step2. use Delete interface to delete (k1, v1) and Get the value of k1. + * @tc.expected: step2. delete successfully and can't find k1 in db. + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), NOT_FOUND); + + /** + * @tc.steps: step3. Rollback and check the data in db again. + * @tc.expected: step3. Rollback successfully and can't find (k1, v1) and (k2, v2) in db. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), NOT_FOUND); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, value), NOT_FOUND); + + /** + * @tc.steps: step4. Rollback again. + * @tc.expected: step4. returned error. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), DB_ERROR); +} + +/** + * @tc.name: Transaction 003 + * @tc.desc: verify that it can't start transaction repeatedly. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction003, TestSize.Level0) +{ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + /** + * @tc.steps: step1. start transaction and update (k1, v1) to (k1, v2). + * @tc.expected: step1. start and put successfully but the value of k1 is still v1. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_2), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_2); + /** + * @tc.steps: step2. start the transaction again. + * @tc.expected: step2. returned error. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), DB_ERROR); + + /** + * @tc.steps: step3. commit and check the records in db. + * @tc.expected: step3. commit successfully and the value of k1 is v2. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_2); +} + +/** + * @tc.name: Transaction 004 + * @tc.desc: verify that can't commit without start transaction and can putBatch when start transaction. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction004, TestSize.Level0) +{ + /** + * @tc.steps: step1. commit the transaction without start it. + * @tc.expected: step1. commit failed. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), DB_ERROR); + /** + * @tc.steps: step2. start the transaction and putBatch 128 records and check the data with GetEntries. + * @tc.expected: step2. start success and GetEntries returns no entry. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + std::vector entries, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, BATCH_RECORDS); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_EQ(entriesGot.size(), BATCH_RECORDS); + + /** + * @tc.steps: step3. commit and GetEntries in db. + * @tc.expected: step3. commit successfully and can find the entries in db. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); +} + +/** + * @tc.name: Transaction 005 + * @tc.desc: verify that can't Rollback without start transaction and can updateBatch when start transaction. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction005, TestSize.Level0) +{ + /** + * @tc.steps: step1. Rollback the transaction without start it and PutBatch 10 records to DB. + * @tc.expected: step1. Rollback failed and PutBatch success. + */ + std::vector entries1, entries2, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, TEN_RECORDS, {'k'}, {'a'}); + GenerateAppointPrefixAndSizeRecords(entries2, entrySize, TEN_RECORDS, {'k'}, {'b'}); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), DB_ERROR); + /** + * @tc.steps: step2. start the transaction and update the 10 records Batchly and check the data with GetEntries. + * @tc.expected: step2. start success and GetEntries return the entries is equal to entres1. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries2, entriesGot)); + + /** + * @tc.steps: step3. commit and GetEntries in db. + * @tc.expected: step3. commit successfully and the entriesGot is equal to entries2. + */ + entriesGot.clear(); + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries2, entriesGot)); +} + +/** + * @tc.name: Transaction 006 + * @tc.desc: verify that can DeleteBatch when start transaction. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction006, TestSize.Level0) +{ + std::vector entries1, entries2, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, TEN_RECORDS, {'k'}, {'a'}); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + + for (unsigned int index = 0; index < (entries1.size() / 2); index++) { + entries2.push_back(entries1[index]); + } + std::vector keys; + for (auto const &entry : entries2) { + keys.push_back(entry.key); + } + + /** + * @tc.steps: step1. start the transaction and Deleted Batchly 5 of the 10 records inserted to db. + * @tc.expected: step1. start and delete success and GetEntries return the entries is equal to entres1. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + entries1.erase(entries1.begin(), entries1.begin() + FIVE_RECORDS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, entriesGot)); + + /** + * @tc.steps: step2. commit and GetEntries in db. + * @tc.expected: step2. commit successfully and the entriesGot is equal to (entries1 - entries2). + */ + entriesGot.clear(); + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, entriesGot)); + + /** + * @tc.steps: step3. commit again. + * @tc.expected: step3. commit failed and returned error. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), DB_ERROR); +} + +/** + * @tc.name: Transaction 007 + * @tc.desc: verify that transaction support native db insert or delete records + * but don't support local db to operate db. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction007, TestSize.Level0) +{ + /** + * @tc.steps: step1. PutLocal (k1, v1) to Local DB and start transaction + * @tc.expected: step1. putLocal and start success. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + + /** + * @tc.steps: step2. start the transaction and Put and update and deleteLocal on DB and commit the transaction. + * @tc.expected: step2. start and Put and commit success, + * but can't find records on native db, and deletelocal returned failed. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_2); + + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocal(*g_nbBatchCrudDelegate, KEY_1), OK); + + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_2); + EXPECT_EQ(DistributedDBNbTestTools::GetLocal(*g_nbBatchCrudDelegate, KEY_1, value), NOT_FOUND); + + /** + * @tc.steps: step3. Rollback after commit. + * @tc.expected: step3. Rollback failed and returned error. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), DB_ERROR); +} + +/** + * @tc.name: Transaction 008 + * @tc.desc: verify that it can't commit after it was Rollbacked + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction008, TestSize.Level0) +{ + /** + * @tc.steps: step1. Put (k1, v1) to DB and start transaction + * @tc.expected: step1. put and start success. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. update (k1, v1) and Rollback. + * @tc.expected: step2. update and Rollback successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_2), OK); + + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_2); + + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); + /** + * @tc.steps: step3. commit after Rollback and check the records in db. + * @tc.expected: step3. commit failed and returned error and the value of k1 is v1. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), DB_ERROR); + + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); +} + +/** + * @tc.name: Transaction 009 + * @tc.desc: verify that start transaction and commit or rollback repeatedly + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction009, TestSize.Level0) +{ + /** + * @tc.steps: step1. repeat 5 times + * @tc.expected: step1. start loop success. + */ + for (int index = 0; index < FIVE_TIMES; index++) { + /** + * @tc.steps: step2. start transaction and commit. + * @tc.expected: step2. start and commit successfully + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + /** + * @tc.steps: step3. start transaction and rollback. + * @tc.expected: step3. start and Rollback successfully + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); + } +} + +/** + * @tc.name: Transaction 010 + * @tc.desc: verify that can Batchly operate on transaction + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction010, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction + * @tc.expected: step1. start success. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + std::vector entries1, entries2, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, SIXTY_RECORDS, {'a'}, {'m'}); + GenerateAppointPrefixAndSizeRecords(entries2, entrySize, THIRTYTWO_RECORDS, {'b'}, {'n'}); + /** + * @tc.steps: step2. PutBatch 60 records entries1 to DB. + * @tc.expected: step2. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + /** + * @tc.steps: step3. PutBatch 32 records entries2 to DB. + * @tc.expected: step3. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + + std::vector keys; + for (auto const &entry : entries2) { + keys.push_back(entry.key); + } + /** + * @tc.steps: step4. DeleteBatch entries2 from DB. + * @tc.expected: step4. DeleteBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + + /** + * @tc.steps: step5. Delete entries1[1] and entries1[2] from DB solely. + * @tc.expected: step5. Delete successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, entries1[1].key), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, entries1[2].key), OK); + /** + * @tc.steps: step6. insert entries1[1] to DB solely. + * @tc.expected: step6. insert successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, entries1[1].key, entries1[1].value), OK); + /** + * @tc.steps: step7. update value of entries1[1] to v2. + * @tc.expected: step7. update successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, entries1[1].key, VALUE_2), OK); + /** + * @tc.steps: step8. commit and check the records in DB. + * @tc.expected: step8. commit success and can only find entries1 in db can't find entries2 + * and the value of entries1[1] is v2, and also can't find entries[2]. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + for (auto const & entry : entries2) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_EQ(it, entriesGot.end()); + } + for (auto const & entry : entries1) { + if (!CompareVector(entry.key, entries1[1].key) && !CompareVector(entry.key, entries1[2].key)) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_NE(it, entriesGot.end()); + } else if (CompareVector(entry.key, entries1[1].key)) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(VALUE_2, entry_.value)); + }); + EXPECT_NE(it, entriesGot.end()); + } else { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_EQ(it, entriesGot.end()); + } + } +} + +/** + * @tc.name: Transaction 011 + * @tc.desc: verify that when rollback after logs of CRUD operate, the operate will not be effective. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction011, TestSize.Level1) +{ + std::vector entries1, entries2, entriesGot; + entries1 = {ENTRY_1, ENTRY_2, ENTRY_3}; + entries2 = {ENTRY_4, ENTRY_5}; + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + /** + * @tc.steps: step1. start transaction + * @tc.expected: step1. start success. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + entries1[0] = {KEY_1, VALUE_EMPTY}; + entries1[1] = {KEY_2, VALUE_EMPTY}; + entries1[2] = {KEY_3, VALUE_EMPTY}; + /** + * @tc.steps: step2. PutBatch entries1 to DB. + * @tc.expected: step2. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + /** + * @tc.steps: step3. PutBatch entries2 to DB. + * @tc.expected: step3. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + + std::vector keys = {KEY_4, KEY_5}; + /** + * @tc.steps: step4. DeleteBatch entries2 from DB. + * @tc.expected: step4. DeleteBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteBatch(*g_nbBatchCrudDelegate, keys), OK); + + /** + * @tc.steps: step5. Delete entries1[1] and entries1[2] from DB solely. + * @tc.expected: step5. Delete successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, entries1[1].key), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, entries1[2].key), OK); + /** + * @tc.steps: step6. insert entries1[1] to DB solely. + * @tc.expected: step6. insert successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, entries1[1].key, entries1[1].value), OK); + /** + * @tc.steps: step7. update value of entries1[1] to v2. + * @tc.expected: step7. update successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, entries1[1].key, VALUE_2), OK); + /** + * @tc.steps: step8. insert the records that key = null. + * @tc.expected: step8. insert failed and returned DB_ERROR + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_EMPTY, VALUE_9), INVALID_ARGS); + + /** + * @tc.steps: step9. rollback and use GetEntries interface to check. + * @tc.expected: step9. rollback success and the data in db is entries1 + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); + entries1 = {ENTRY_1, ENTRY_2, ENTRY_3}; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, entriesGot)); +} + +/** + * @tc.name: Transaction 012 + * @tc.desc: verify that exception operate can't effect transaction and commit. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction012, TestSize.Level1) +{ + std::vector entries, entriesGot; + entries = {ENTRY_1, ENTRY_2}; + /** + * @tc.steps: step1. start transaction + * @tc.expected: step1. start success. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. PutBatch entries to DB. + * @tc.expected: step2. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); + /** + * @tc.steps: step3. Put the record that key = empty to DB. + * @tc.expected: step3. Put failed and returned error + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_EMPTY, VALUE_10), INVALID_ARGS); + + /** + * @tc.steps: step4. commit and use GetEntries interface to check. + * @tc.expected: step4. commit success and the data in db is entries1 + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); +} + +/** + * @tc.name: Transaction 013 + * @tc.desc: verify that one transaction can only operate less than MAX_BATCH_SIZE records, or it will return + * OVER_MAX_LIMITS. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction013, TestSize.Level1) +{ + std::vector entries, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, BATCH_RECORDS); + /** + * @tc.steps: step1. start transaction + * @tc.expected: step1. start success. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. PutBatch entries which contains 128 records to DB. + * @tc.expected: step2. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); + /** + * @tc.steps: step3. Put (k1, v1) to DB. + * @tc.expected: step3. Put failed and returned OVER_MAX_LIMITS + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OVER_MAX_LIMITS); + /** + * @tc.steps: step3. Delete (k1, v1) from DB. + * @tc.expected: step3. Delete failed and returned OVER_MAX_LIMITS + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OVER_MAX_LIMITS); + + /** + * @tc.steps: step4. commit and use GetEntries interface to check. + * @tc.expected: step4. commit success and the data in db is entries1 + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); +} + +/** + * @tc.name: Transaction 014 + * @tc.desc: verify that one transaction can only operate less than MAX_BATCH_SIZE records, or it will return + * OVER_MAX_LIMITS and if one operate return OVER_MAX_LIMITS, only this operate is invalid. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction014, TestSize.Level1) +{ + std::vector entries1, entries2, entries3, entries4, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, ONE_HUNDRED_RECORDS, {'a'}, {'m'}); + GenerateAppointPrefixAndSizeRecords(entries2, entrySize, RECORDS_SMALL_CNT, {'b'}, {'n'}); + GenerateAppointPrefixAndSizeRecords(entries3, entrySize, THIRTYTWO_RECORDS, {'c'}, {'o'}); + GenerateAppointPrefixAndSizeRecords(entries4, entrySize, TWENTYFIVE_RECORDS, {'d'}, {'p'}); + /** + * @tc.steps: step1. start transaction + * @tc.expected: step1. start success. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. PutBatch 100 records to DB. + * @tc.expected: step2. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, entriesGot)); + /** + * @tc.steps: step3. PutBatch 2 records to DB. + * @tc.expected: step3. PutBatch successfully + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(entriesGot.size() == ONE_HUNDRED_RECORDS + RECORDS_SMALL_CNT); + /** + * @tc.steps: step4. Delete nonexistent key1 from DB. + * @tc.expected: step4. Delete return OK, and the number of records is still counted in the transaction. + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(entriesGot.size() == ONE_HUNDRED_RECORDS + RECORDS_SMALL_CNT); + const int nonexistentRecordNum = 1; + /** + * @tc.steps: step5. PutBatch 32 records to DB. + * @tc.expected: step5. PutBatch failed and OVER_MAX_LIMITS + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries3), OVER_MAX_LIMITS); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(entriesGot.size() == ONE_HUNDRED_RECORDS + RECORDS_SMALL_CNT); + /** + * @tc.steps: step5. PutBatch 26 records to DB. + * @tc.expected: step5. PutBatch succeed. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries4), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(entriesGot.size() == (BATCH_RECORDS - nonexistentRecordNum)); + + /** + * @tc.steps: step4. commit and use GetEntries interface to check. + * @tc.expected: step4. commit success and the data in db is entries1 + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + for (auto const & entry : entries4) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_NE(it, entriesGot.end()); + } + for (auto const & entry : entries1) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_NE(it, entriesGot.end()); + } + for (auto const & entry : entries2) { + auto it = std::find_if(entriesGot.begin(), entriesGot.end(), [entry](const DistributedDB::Entry &entry_) { + return (CompareVector(entry.key, entry_.key) && CompareVector(entry.value, entry_.value)); + }); + EXPECT_NE(it, entriesGot.end()); + } +} + +int g_threadComplete = 0; +bool g_threadSuccessFlag = true; +void ConsistencyCheck() +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(delegateManager, g_dbParameter1, g_option); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "ConsistencyCheck get delegate failed."; + + while (g_threadComplete < static_cast(SINGLE_THREAD_NUM)) { + std::vector entriesGot; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*delegate, KEY_EMPTY, entriesGot), OK); + if (GetIntValue(entriesGot[0].value) + GetIntValue(entriesGot[1].value) != VALUE_SUM) { + g_threadSuccessFlag = false; + MST_LOG("ConsistencyCheck get sum %d,%d.", GetIntValue(entriesGot[0].value), + GetIntValue(entriesGot[1].value)); + break; + } + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + } + + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +void ConsistencyChange(Key keyLeft, Value valueLeft, Key keyRight, Value valueRight) +{ + // wait 100 ms, let ConsistencyCheck begin + std::this_thread::sleep_for(std::chrono::duration(HUNDRED_MILLI_SECONDS)); + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(delegateManager, g_dbParameter1, g_option); + if (delegate == nullptr) { + MST_LOG("ConsistencyChange get delegate failed."); + g_threadComplete++; + g_threadSuccessFlag = false; + return; + } + + MST_LOG("ConsistencyChange put %d,%d.", GetIntValue(valueLeft), GetIntValue(valueRight)); + DBStatus statusStart = delegate->StartTransaction(); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + + DBStatus statusPutLeft = delegate->Put(keyLeft, valueLeft); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + + DBStatus statusPutRight = delegate->Put(keyRight, valueRight); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + + DBStatus statusCommit = delegate->Commit(); + std::this_thread::sleep_for(std::chrono::duration(FIFTY_MILI_SECONDS)); + g_threadComplete++; + + if (statusStart != DBStatus::OK || statusPutLeft != DBStatus::OK || + statusPutRight != DBStatus::OK || statusCommit != DBStatus::OK) { + MST_LOG("ConsistencyChange put failed."); + g_threadComplete++; + g_threadSuccessFlag = false; + return; + } + ConsistencyCheck(); + + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +/** + * @tc.name: Transaction 015 + * @tc.desc: Verify that transaction singlely action's consistency. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction015, TestSize.Level2) +{ + int baseNumer = VALUE_FIVE_HUNDRED; + Value base = GetValueWithInt(baseNumer); + + Entry entry1, entry2; + vector entries1; + entry1 = {KEY_CONS_1, base}; + entry2 = {KEY_CONS_2, base}; + entries1.push_back(entry1); + entries1.push_back(entry2); + /** + * @tc.steps: step1. putBatch (k1=cons1,v1=500) (k2=cons2,v2=500) to construct exist v1+v2=1000. + * @tc.expected: step1. putBatch successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, entry1.key, value), OK); + EXPECT_EQ(value, base); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, entry2.key, value), OK); + EXPECT_EQ(value, base); + + /** + * @tc.steps: step2. start a thread to update k1=cons1's value to 400, update k2=cons2's value to 600. + * @tc.expected: step2. run thread successfully and k1+k2=1000 is true in sub thread. + */ + std::vector suThreads; + suThreads.push_back(std::thread(ConsistencyChange, KEY_CONS_1, GetValueWithInt(VALUE_CHANGE1_FIRST), + KEY_CONS_2, GetValueWithInt(VALUE_CHANGE1_SECOND))); + /** + * @tc.steps: step3. start another thread to update k1=cons1's value to 700, update k2=cons2's value to 300. + * @tc.expected: step3. run thread successfully and k1+k2=1000 is true in sub thread. + */ + suThreads.push_back(std::thread(ConsistencyChange, KEY_CONS_1, GetValueWithInt(VALUE_CHANGE2_FIRST), + KEY_CONS_2, GetValueWithInt(VALUE_CHANGE2_SECOND))); + for (auto& th : suThreads) { + th.detach(); + } + + ConsistencyCheck(); + std::this_thread::sleep_for(std::chrono::seconds(FIVE_SECONDS)); + ASSERT_TRUE(g_threadSuccessFlag); +} + +bool g_batchThreadSuccess = true; +bool g_batchThreadComplete = false; +void ConsistencyBatchCheck(vector *keyLeft, vector *keyRight, int times) +{ + std::this_thread::sleep_for(std::chrono::seconds(TWO_SECONDS)); + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *delegateManager = nullptr; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(delegateManager, g_dbParameter1, g_option); + ASSERT_TRUE(delegateManager != nullptr && delegate != nullptr) << "ConsistencyBatchCheck get delegate failed."; + + Value leftValue, rightValue; + while (!g_batchThreadComplete) { + for (int i = 0; i < times; ++i) { + leftValue.clear(); + rightValue.clear(); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, keyLeft->at(i), leftValue), OK); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, keyRight->at(i), rightValue), OK); + + if (GetIntValue(leftValue) + GetIntValue(rightValue) != (times + 1)) { + g_batchThreadSuccess = false; + MST_LOG("ConsistencyBatchCheck get leftvalue: %d, rightvalue: %d, failed.", + GetIntValue(leftValue), GetIntValue(rightValue)); + break; + } + } + } + + MST_LOG("g_batchThreadComplete."); + EXPECT_EQ(delegateManager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete delegateManager; + delegateManager = nullptr; +} + +void ConsistencyCheckTransaction(vector &entries1, vector &entries2) +{ + Key query1 = {'k', 'a'}; + Key query2 = {'k', 'b'}; + /** + * @tc.steps: step1. putBatch 20 items of (keys1,values1)(key2,values2). + * @tc.expected: step1. putBatch successfully to construct exist data in db. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + + /** + * @tc.steps: step3. start transaction. + * @tc.expected: step3. start transaction successfully. + */ + EXPECT_TRUE(g_nbBatchCrudDelegate->StartTransaction() == DBStatus::OK); + /** + * @tc.steps: step4. getEntries with keyprefix before updateBatch. + * @tc.expected: step4. getEntries successfully that the size of them is 20. + */ + vector values1, values2; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, query1, values1), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, query2, values2), OK); + EXPECT_EQ(static_cast(values1.size()), TWENTY_RECORDS); + EXPECT_EQ(static_cast(values2.size()), TWENTY_RECORDS); + /** + * @tc.steps: step5. updateBatch values1+1 of keys1, values2-1 of keys2. + * @tc.expected: step5. updateBatch successfully. + */ + for (int i = 0; i != TWENTY_RECORDS; ++i) { + entries1[i].value = GetValueWithInt(GetIntValue(values1[i].value) + 1); + entries2[i].value = GetValueWithInt(GetIntValue(values2[i].value) - 1); + } + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + /** + * @tc.steps: step6. updateBatch values1+2 of keys1, values2-2 of keys2. + * @tc.expected: step6. updateBatch successfully. + */ + for (int i = 0; i != TWENTY_RECORDS; ++i) { + entries1[i].value = GetValueWithInt(GetIntValue(values1[i].value) + EVEN_NUMBER); + entries2[i].value = GetValueWithInt(GetIntValue(values2[i].value) - EVEN_NUMBER); + } + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries2), OK); + /** + * @tc.steps: step7. commit transaction and stop child thread. + * @tc.expected: step7. operate successfully. + */ + EXPECT_TRUE(g_nbBatchCrudDelegate->Commit() == DBStatus::OK); + MST_LOG("commit over!"); +} + +/** + * @tc.name: Transaction 016 + * @tc.desc: Verify that transaction batch action's consistency. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction016, TestSize.Level2) +{ + uint8_t left = 'a'; + uint8_t right = 'b'; + vector entries1; + vector allKeys1; + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys1, entries1); + vector entries2; + vector allKeys2; + GenerateRecords(TWENTY_RECORDS, DEFAULT_START, allKeys2, entries2); + for (int i = 0; i < TWENTY_RECORDS; ++i) { + entries1[i].key.insert(entries1[i].key.begin() + 1, left); + entries2[i].key.insert(entries2[i].key.begin() + 1, right); + allKeys1[i].insert(allKeys1[i].begin() + 1, left); + allKeys2[i].insert(allKeys2[i].begin() + 1, right); + entries1[i].value = GetValueWithInt(i); + entries2[i].value = GetValueWithInt(TWENTY_RECORDS - i); + } + + /** + * @tc.steps: step2. start child thread to query keys1 and keys2 continuously. + * @tc.expected: step2. if keys1.size()+keys2.size()!=20 print info. + */ + thread readThread = thread(ConsistencyBatchCheck, &allKeys1, &allKeys2, TWENTY_RECORDS); + readThread.detach(); + ConsistencyCheckTransaction(entries1, entries2); + g_batchThreadComplete = true; + + std::this_thread::sleep_for(std::chrono::duration(MILLSECONDES_PER_SECOND)); + ASSERT_TRUE(g_batchThreadSuccess); +} + +void TransactionSubThread1() +{ + /** + * @tc.steps: step2. start transanction and update v1 of k1 to v2, and put (k3, v3) to STORE_ID_1. + * @tc.expected: step2. start and put successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_3, VALUE_3), OK); +} +void TransactionSubThread2() +{ + std::this_thread::sleep_for(std::chrono::duration(MILLSECONDES_PER_SECOND)); + + /** + * @tc.steps: step3. update v2 of k2 to v3, and delete k1, and Get (k3, v3) from DB + * @tc.expected: step3. put and delete successfully, but can't find (k3, v3) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_3), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_3, value), OK); + EXPECT_EQ(value, VALUE_3); +} +void TransactionSubThread3() +{ + std::this_thread::sleep_for(std::chrono::duration(MILLSECONDES_PER_SECOND)); + + /** + * @tc.steps: step4. rollback + * @tc.expected: step4. rollback successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); +} +/** + * @tc.name: Transaction 017 + * @tc.desc: Verify that one delegate start transaction, and then the different delegate of the same storeid are + * already in the same transaction. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction017, TestSize.Level2) +{ + /** + * @tc.steps: step1. put (k1, v1), (k2, v2) to STORE_ID_1. + * @tc.expected: step1. put successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_2), OK); + + std::thread thread1, thread2, thread3; + thread1 = std::thread(TransactionSubThread1); + thread1.join(); + thread2 = std::thread(TransactionSubThread2); + thread2.join(); + thread3 = std::thread(TransactionSubThread3); + thread3.join(); + + /** + * @tc.steps: step5. check the records in db + * @tc.expected: step5. the value of k1 is v1, the value of k2 is v2, but (k3, v3) is not in db. + */ + Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_2, value), OK); + EXPECT_EQ(value, VALUE_2); + value.clear(); + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_3, value), NOT_FOUND); + EXPECT_TRUE(value.empty()); +} + +/** + * @tc.name: Transaction 018 + * @tc.desc: Verify that transaction has persistence. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction018, TestSize.Level1) +{ + KvStoreNbDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + delegate1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter2, g_option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr) << "get delegate1 failed."; + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transactionsuccessfully. + */ + EXPECT_EQ(delegate1->StartTransaction(), OK); + /** + * @tc.steps: step2. putBatch (k1, v1), (k2, v2) to STORE_ID_1. + * @tc.expected: step2. putBatch successfully. + */ + std::vector entries = {ENTRY_1, ENTRY_2}; + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*delegate1, entries), OK); + + /** + * @tc.steps: step3. commit the transaction. + * @tc.expected: step3. commit successfully. + */ + EXPECT_EQ(delegate1->Commit(), OK); + /** + * @tc.steps: step4. close the db but don't delete the STORE, and then create a new delegate of the same STORE + * @tc.expected: step4. close and create successfully. + */ + EXPECT_EQ(manager1->CloseKvStore(delegate1), OK); + delegate1 = nullptr; + delete manager1; + manager1 = nullptr; + + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, g_option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr) << "get delegate2 failed."; + /** + * @tc.steps: step5. check the data in db STORE. + * @tc.expected: step5. the entries is still in the db. + */ + std::vector entriesGot; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*delegate2, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entriesGot, entries)); + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} + +/** + * @tc.name: Transaction 019 + * @tc.desc: Verify that close the delegate before transaction commit, and it will cause the transaction rollback. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction019, TestSize.Level1) +{ + KvStoreNbDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + delegate1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter2, g_option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr) << "get delegate1 failed."; + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transactionsuccessfully. + */ + EXPECT_EQ(delegate1->StartTransaction(), OK); + /** + * @tc.steps: step2. putBatch (k1, v1), (k2, v2) to STORE_ID_1. + * @tc.expected: step2. putBatch successfully. + */ + std::vector entries = {ENTRY_1, ENTRY_2}; + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*delegate1, entries), OK); + + /** + * @tc.steps: step3. close the db but don't delete the STORE without committing the transaction + * @tc.expected: step3. close successfully. + */ + EXPECT_EQ(manager1->CloseKvStore(delegate1), OK); + delegate1 = nullptr; + delete manager1; + manager1 = nullptr; + + /** + * @tc.steps: step4. create a new delegate of the same STORE + * @tc.expected: step4. start successfully. + */ + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, g_option); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr) << "get delegate2 failed."; + /** + * @tc.steps: step5. commit the transaction on the new deleate, and check the data in db STORE. + * @tc.expected: step5. commit failed and the entries is NULL. + */ + EXPECT_EQ(delegate2->Commit(), DB_ERROR); + std::vector entriesGot; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*delegate2, KEY_EMPTY, entriesGot), NOT_FOUND); + EXPECT_TRUE(entriesGot.empty()); + + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} + +/** + * @tc.name: Transaction 020 + * @tc.desc: Verify that close the delegate before transaction rollback, and it will cause the transaction rollback. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction020, TestSize.Level1) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transactionsuccessfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. putBatch (k1, v1), (k2, v2) to STORE_ID_1. + * @tc.expected: step2. putBatch successfully. + */ + std::vector entries = {ENTRY_1, ENTRY_2}; + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + + /** + * @tc.steps: step3. close the db but don't delete the STORE without committing the transaction + * @tc.expected: step3. close successfully. + */ + EXPECT_EQ(g_manager->CloseKvStore(g_nbBatchCrudDelegate), OK); + g_nbBatchCrudDelegate = nullptr; + delete g_manager; + g_manager = nullptr; + + /** + * @tc.steps: step4. create a new delegate of the same STORE + * @tc.expected: step4. start successfully. + */ + g_nbBatchCrudDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_nbBatchCrudDelegate != nullptr) << "get g_nbBatchCrudDelegate failed."; + /** + * @tc.steps: step5. Rollback the transaction on the new deleate, and check the data in db STORE. + * @tc.expected: step5. Rollback failed and the entries is NULL. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), DB_ERROR); + std::vector entriesGot; + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), NOT_FOUND); + EXPECT_TRUE(entriesGot.empty()); +} + +/** + * @tc.name: Transaction 021 + * @tc.desc: Verify that it will failed to delete the store before transaction rollback. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction021, TestSize.Level0) +{ + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transactionsuccessfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. put (k1, v1) to STORE_ID_1. + * @tc.expected: step2. put successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + DistributedDB::Value value; + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), OK); + EXPECT_EQ(value, VALUE_1); + + /** + * @tc.steps: step3. delete the STORE without committing or rollback + * the transaction + * @tc.expected: step3. delete failed and returned BUSY. + */ + EXPECT_EQ(g_manager->DeleteKvStore(STORE_ID_1), BUSY); + + /** + * @tc.steps: step4. Rollback and check the db + * @tc.expected: step4. Rollback failed. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rollback(), OK); + + /** + * @tc.steps: step5. check the data in db STORE. + * @tc.expected: step5. the DB is empty. + */ + EXPECT_EQ(DistributedDBNbTestTools::Get(*g_nbBatchCrudDelegate, KEY_1, value), NOT_FOUND); +} + +/** + * @tc.name: Transaction 022 + * @tc.desc: Verify transaction can't support resultSet, rekey, Import, Export, RegisterObserver interface. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, Transaction022, TestSize.Level3) +{ + KvStoreResultSet *resultSet1 = nullptr; + KvStoreResultSet *resultSet2 = nullptr; + EXPECT_EQ(g_nbBatchCrudDelegate->GetEntries(KEY_EMPTY, resultSet1), OK); + KvStoreObserverImpl observer1, observer2; + vector entries, entriesGot; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, TEN_RECORDS); + + DBStatus status = g_nbBatchCrudDelegate->RegisterObserver(KEY_K, OBSERVER_CHANGES_NATIVE, &observer1); + EXPECT_EQ(status, OK); + + const std::string importPath = NB_DIRECTOR + "import"; + const std::string importFilePath = importPath + "/importbkpDB.bin"; + SetDir(importPath); + EXPECT_EQ(g_nbBatchCrudDelegate->Export(importFilePath, NULL_PASSWD), OK); + + /** + * @tc.steps: step1. start transaction. + * @tc.expected: step1. start transaction successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. putBatch entries to db. + * @tc.expected: step2. putBatch successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); + + vector observerCheckEntry; + observerCheckEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, ListType::INSERT_LIST, observerCheckEntry)); + + /** + * @tc.steps: step3. call rekey, import, export interface + * @tc.expected: step3. all call failed and returned BUSY. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Rekey(g_passwd1), BUSY); + + const std::string exportPath = NB_DIRECTOR + "export"; + const std::string filePath = exportPath + "/bkpDB.bin"; + SetDir(exportPath); + EXPECT_EQ(g_nbBatchCrudDelegate->Export(filePath, NULL_PASSWD), BUSY); + EXPECT_EQ(g_nbBatchCrudDelegate->Import(importFilePath, NULL_PASSWD), BUSY); + /** + * @tc.steps: step4. call GetEntries to obtain resultSet2, and register observer2. + * @tc.expected: step4. all call failed and returned BUSY. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->GetEntries(KEY_EMPTY, resultSet2), BUSY); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_K, OBSERVER_CHANGES_NATIVE, &observer2), BUSY); + + /** + * @tc.steps: step5. close resultSet1 and resultSet2, UnRegister observer1 and observer2. + * @tc.expected: step5. close resultSet1 succeed and resultSet2 failed, + * UnRegister observer1 succeed and observer2 failed. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->CloseResultSet(resultSet1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->CloseResultSet(resultSet2), INVALID_ARGS); + EXPECT_EQ(g_nbBatchCrudDelegate->UnRegisterObserver(&observer1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->UnRegisterObserver(&observer2), NOT_FOUND); + /** + * @tc.steps: step6. commit the transaction and check the data in db. + * @tc.expected: step6. commit succeed and can find entries in DB. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + EXPECT_EQ(DistributedDBNbTestTools::GetEntries(*g_nbBatchCrudDelegate, KEY_EMPTY, entriesGot), OK); + EXPECT_TRUE(CompareEntriesVector(entries, entriesGot)); +} + +/** + * @tc.name: TransactionObserver 001 + * @tc.desc: Verify transaction can and only trigger observer one time. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, TransactionObserver001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Register observer1 and observer2 of k1 and k2 separately and start the transaction. + * @tc.expected: step1. Register start transaction successfully. + */ + KvStoreObserverImpl observer1, observer2; + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + /** + * @tc.steps: step2. put (k1, v1), (k3, v3) and check the callback of the observer. + * @tc.expected: step2. put successfully and both of the observers can't receive the callback. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_3, VALUE_3), OK); + vector observerCheckList; + observerCheckList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, observerCheckList)); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, INSERT_LIST, observerCheckList)); + /** + * @tc.steps: step3. update (k1, v1) to (k1, v2) and delete (k3, v3) and check the observers. + * @tc.expected: step3. update and delete successfully both of the observers can't receive the callback either. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_3), OK); + observerCheckList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, observerCheckList)); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, DELETE_LIST, observerCheckList)); + + /** + * @tc.steps: step4. commit the transaction and check the observers. + * @tc.expected: step4. commit succeed and observer1 was triggered one time and observer2 wasn't triggered. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, observerCheckList)); + observerCheckList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ZERO_TIME, DELETE_LIST, observerCheckList)); +} + +/** + * @tc.name: TransactionObserver 002 + * @tc.desc: Verify transaction can and only trigger observer one time. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, TransactionObserver002, TestSize.Level1) +{ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_2), OK); + /** + * @tc.steps: step1. Register observer1, observer2, observer3, observer4 of k1, k2, k3 + * and k4 separately and start the transaction. + * @tc.expected: step1. Register and start transaction successfully. + */ + KvStoreObserverImpl observer1, observer2, observer3, observer4; + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer1), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observer2), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_NATIVE, &observer3), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_4, OBSERVER_CHANGES_NATIVE, &observer4), OK); + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + + /** + * @tc.steps: step2. put (k1, v1), (k2, v2), (k3, v3) and (k4, v4) and then delete (k1, v1) and (k4, v4). + * @tc.expected: step2. put and delete successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_3), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_3, VALUE_3), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_4, VALUE_4), OK); + + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_4), OK); + + /** + * @tc.steps: step3. commit the transaction and check the observers. + * @tc.expected: step3. commit succeed and observer1 received one delete notify, observer2 received one update + * notify, observer3 received one insert notify, and observer4 didn't receive any notify. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + vector observerCheckList; + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, observerCheckList)); + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, UPDATE_LIST, observerCheckList)); + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ONE_TIME, INSERT_LIST, observerCheckList)); + observerCheckList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer4, CHANGED_ZERO_TIME, DELETE_LIST, observerCheckList)); +} + +/** + * @tc.name: TransactionObserver 003 + * @tc.desc: Verify the different observer of same key will all be triggered after transaction + * and will only be trigger once. + * @tc.type: FUNC + * @tc.require: SR000DORPP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbBatchCrudTest, TransactionObserver003, TestSize.Level1) +{ + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_2), OK); + /** + * @tc.steps: step1. Register 8 observers of all keys. + * @tc.expected: step1. Register successfully. + */ + std::vector observers(OBSERVER_NUM); + for (unsigned long cnt = 0; cnt < OBSERVER_NUM; cnt++) { + EXPECT_EQ(g_nbBatchCrudDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, &observers[cnt]), OK); + } + + /** + * @tc.steps: step2. start the transaction and putBatch (k3, v3), (k4, v4), ..., (k8, v8) to db. + * @tc.expected: step2. start transaction and putBatch successfully. + */ + EXPECT_EQ(g_nbBatchCrudDelegate->StartTransaction(), OK); + std::vector entries = {ENTRY_3, ENTRY_4, ENTRY_5, ENTRY_6, ENTRY_7, ENTRY_8, ENTRY_9}; + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbBatchCrudDelegate, entries), OK); + + /** + * @tc.steps: step3. delete (k1, v1), (k3, v3) and (k9, v9), update (k2, v2) to (k2, v3). + * @tc.expected: step3. operate succeed. + */ + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::Put(*g_nbBatchCrudDelegate, KEY_2, VALUE_3), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_3), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*g_nbBatchCrudDelegate, KEY_9), OK); + + /** + * @tc.steps: step3. commit the transaction and check the observers. + * @tc.expected: step3. commit succeed and all of the observers received one insert notify, callbacklist of which + * contains (k4, v4), ..., (k8, v8), all observer received one update notify, callbacklist of which contains + * (k2, v3), all observers received one insert notify, callback of which contains (k1, v1), (k3, v3), (k9, v9). + */ + EXPECT_EQ(g_nbBatchCrudDelegate->Commit(), OK); + vector observerCheckList; + for (unsigned long cnt = 0; cnt < OBSERVER_NUM; cnt++) { + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_4); + observerCheckList.push_back(ENTRY_5); + observerCheckList.push_back(ENTRY_6); + observerCheckList.push_back(ENTRY_7); + observerCheckList.push_back(ENTRY_8); + EXPECT_TRUE(VerifyObserverResult(observers[cnt], CHANGED_ONE_TIME, INSERT_LIST, observerCheckList)); + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observers[cnt], CHANGED_ONE_TIME, UPDATE_LIST, observerCheckList)); + observerCheckList.clear(); + observerCheckList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observers[cnt], CHANGED_ONE_TIME, DELETE_LIST, observerCheckList)); + } +} +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_create_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_create_test.cpp new file mode 100755 index 000000000..4511c646e --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_create_test.cpp @@ -0,0 +1,1913 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#include "distributeddb_data_generator.h" +#include "distributeddb_nb_test_tools.h" +#include "kv_store_delegate_manager.h" +#include "types.h" +#include "distributed_test_tools.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; +using namespace std::placeholders; +static std::condition_variable g_conditionNbVar; + +namespace DistributedDBNbCreate { +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; +class DistributeddbNbCreateTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DistributeddbNbCreateTest::SetUpTestCase(void) +{ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); +} + +void DistributeddbNbCreateTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbCreateTest::SetUp(void) +{ + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbNbCreateTest::TearDown(void) +{ +} + +const KvStoreConfig CONFIG = { + .dataDir = NB_DIRECTOR +}; + +void WaitingDeletingDB() +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + /** + * @tc.steps: step2. delete db when kill the process manually. + * @tc.expected: step2. delete success. + */ + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + } + MST_LOG("please kill the testing process..."); + std::this_thread::sleep_for(std::chrono::seconds(TWENTY_SECONDS)); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 001 + * @tc.desc: Verify that can create distributed db normally. + * @tc.type: FUNC + * @tc.require: SR000CQDV2 + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb001, TestSize.Level1) +{ + const std::string storeId[ID_MIN_CNT] = { "STORE_ID_1", "STORE_ID_2" }; + const std::string appId[ID_MIN_CNT] = { "APP_ID_1", "APP_ID_2" }; + const std::string userId[ID_MIN_CNT] = { "USER_ID_1", "USER_ID_2" }; + + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. create NBdelegate with different normal appId, userId and create db with storeId. + * @tc.expected: step1. each db can be created successfully. + */ + for (unsigned int xCnt = ID_CNT_START; xCnt < ID_MIN_CNT; xCnt++) { + for (unsigned int yCnt = ID_CNT_START; yCnt < ID_MIN_CNT; yCnt++) { + for (unsigned int zCnt = ID_CNT_START; zCnt < ID_MIN_CNT; zCnt++) { + const DBParameters dbParameters(storeId[xCnt], appId[yCnt], userId[zCnt]); + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, dbParameters, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, result, storeId[xCnt], g_option.isMemoryDb)); + } + } + } +} + +/* + * @tc.name: ManagerDb 002 + * @tc.desc: Verify that can create distributed with inexistence appid and userid. + * @tc.type: FUNC + * @tc.require: SR000CQDV2 + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb002, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. create NBdelegate with not exist appId, userId and create db with storeId. + * @tc.expected: step1. create db failed. + */ + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, DB_PARAMETER_0_1, g_option); + ASSERT_EQ(result, nullptr); + EXPECT_EQ(manager->CloseKvStore(result), INVALID_ARGS); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID), INVALID_ARGS); + delete manager; + manager = nullptr; + result = nullptr; +} + +/* + * @tc.name: ManagerDb 003 + * @tc.desc: test and verify that Set normal db path and can create distributed. + * @tc.type: FUNC + * @tc.require: SR000CQDV2 + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb003, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. Set normal path create db. + * @tc.expected: step1. db can be successfully created. + */ + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, g_option.isMemoryDb)); +} + +void DefinePath(string &pathOk, string &pathError) +{ + pathOk = + "/data/test/getstub/dddddddddddddddddddddddddddddddddddssssssssssssssfffffDddddddddssssssssssssssssssfffffd/" + "dddddddddddddddddddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/dddddddd" + "dddddddddddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/dddddddddddddddd" + "dddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/dddddddddddddddddddddddd" + "dssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/eserweiaa"; + MST_LOG("pathOk.length() = %zd", pathOk.length()); + pathError = + "/data/test/getstub/ddddddddddddddddddddsssssssssssssfffffDdddddddddddddddddddddddssssssssssssssssssfffffd/" + "qdddddddddddddddddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/qddddddd" + "dddddddddddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/qdddddddddddddd" + "ddddddddddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/qddddddddddddddddddddd" + "dddssssssssssssssssssfffffDdddddddddddddddddddddddddssssssssssssssssssfffffd/qserweiaaaww"; + MST_LOG("pathError.length() = %zd", pathError.length()); + return; +} +/* + * @tc.name: ManagerDb 004 + * @tc.desc: test and verify that Set abnormal db path and can't create distributeddb. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Construct different valid path, which contains, '\0', up letter, low letter, data, + * '_', or '\\' '//' '&' '^' '%' '#' and length equals to 512 characters. invalid path include null, + * longer than 512 characters and not exist path. + * @tc.expected: step1. Construct success. + */ + string pathOk = "", pathError = ""; + DefinePath(pathOk, pathError); + const string nbDirectory[DIR_MAX_CNT] = { + "", "/data/test/getstub/ddddddddddddddddddd/idata/nbstub\0fdfg/", pathOk, pathError, + "/data/test/getstub/ddddddddddddddddddd/idata/..nbstub/", + "/data/test/getstub/ddddddddddddddddddd/idata/…nbstub/", + "/data/test/getstub/ddddddddddddddddddd/idata/分布式数据库/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstubÄäÖöÜü/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub\\\\/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub///", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub&/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub^/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub%/", + "/data/test/getstub/ddddddddddddddddddd/idata/nbstub#/" }; + const string notExistPath = "/data/test/getstub/dddddddddrwddddddd/idqta/nbs"; + DistributedDB::DBStatus resultStatus[DIR_MAX_CNT] = { + INVALID_ARGS, OK, OK, INVALID_ARGS, OK, + OK, OK, OK, OK, OK, + OK, OK, OK, OK }; + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager("APP_ID_1", "USER_ID_1"); + ASSERT_NE(manager, nullptr); + /** + * @tc.steps: step2. Set different path construct above, and create db. + * @tc.expected: step2. return INVALID_ARGS if path invalid, otherwise can return ok. + */ + DBStatus status; + for (unsigned int index = DIR_CNT_START; index < DIR_MAX_CNT; index++) { + MST_LOG("index %d", index); + SetDir(nbDirectory[index]); + KvStoreConfig config = { .dataDir = nbDirectory[index] }; + status = manager->SetKvStoreConfig(config); + MST_LOG("config.dataDir = %s", config.dataDir.c_str()); + MST_LOG("index[%d], status[%d], expect[%d]", index, status, resultStatus[index]); + EXPECT_EQ(status, resultStatus[index]); + chdir("/"); + MST_LOG("\n"); + } + /** + * @tc.steps: step3. verify that path do not exist, and create db. + * @tc.expected: step3. return INVALID_ARGS. + */ + KvStoreConfig config = { .dataDir = notExistPath }; + status = manager->SetKvStoreConfig(config); + MST_LOG("config.dataDir = %s", config.dataDir.c_str()); + EXPECT_EQ(status, INVALID_ARGS); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 006 + * @tc.desc: verify that db can created uses IS_NEED_CREATE mode. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb006, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + SetDir(NB_DIRECTOR); + ASSERT_EQ(manager->SetKvStoreConfig(CONFIG), DBStatus::OK); + ASSERT_EQ(manager->DeleteKvStore(STORE_ID_1), DBStatus::NOT_FOUND); + delete manager; + manager = nullptr; + + /** + * @tc.steps: step1. create db use IS_NEED_CREATE mode. + * @tc.expected: step1. Create success. + */ + KvStoreNbDelegate *result = nullptr; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, g_option.isMemoryDb)); +} + +/* + * @tc.name: ManagerDb 007 + * @tc.desc: verify that db can't be created if it inexist and opened when it exist if it uses IS_NOT_NEED_CREATE mode. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb007, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. db inexist and create it use IS_NOT_NEED_CREATE mode. + * @tc.expected: step1. Create failed. + */ + Option option = g_option; + option.createIfNecessary = IS_NOT_NEED_CREATE; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1_2_1, option); + ASSERT_EQ(manager, nullptr); + ASSERT_EQ(result, nullptr); +} + +/* + * @tc.name: ManagerDb 008 + * @tc.desc: verify that different storeids can create different dbs. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb008, TestSize.Level1) +{ + const std::string storeId[ID_MEDIUM_CNT] = { "STORE_ID_1", "STORE_ID_2", "STORE_ID_3" }; + const std::string appId[ID_MEDIUM_CNT] = { "APP_ID_1", "APP_ID_2", "APP_ID_3" }; + const std::string userId[ID_MEDIUM_CNT] = { "USER_ID_1", "USER_ID_2", "USER_ID_3" }; + /** + * @tc.steps: step1. there isn't db storeId1, storeId2, storeId3, create db with different appIds and userIds. + * @tc.expected: step1. Create db storeId1, storeId2, storeId3 success. + */ + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + SetDir(CONFIG.dataDir); + DBStatus status; + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + for (unsigned int index = 0; index < ID_MEDIUM_CNT; index++) { + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(appId[index], userId[index]); + ASSERT_NE(manager, nullptr); + status = manager->SetKvStoreConfig(CONFIG); + EXPECT_EQ(status, OK); + manager->GetKvStore(storeId[index], option, Nbfunction); + EXPECT_EQ(delegateMgrCallback.GetStatus(), OK); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(storeId[index]), OK); + delete manager; + manager = nullptr; + } +} + +/* + * @tc.name: ManagerDb 009 + * @tc.desc: verify that don't set dir create db failed. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb009, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. didn't set datadir, delegate can create success but db create failed. + * @tc.expected: step1. Create failed. + */ + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + manager->GetKvStore(STORE_ID_1, option, Nbfunction); + EXPECT_NE(delegateMgrCallback.GetStatus(), OK); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), INVALID_ARGS); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 010 + * @tc.desc: verify that different delegate can create different storeid. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb010, TestSize.Level1) +{ + const std::string storeId[STORE_CNT] = { "STORE_ID_1", "STORE_ID_2", "STORE_ID_3", "STORE_ID_4" }; + KvStoreDelegateManager *manager[STORE_CNT] = { nullptr }; + KvStoreNbDelegate *callbackKvStore[STORE_CNT] = { nullptr }; + DelegateMgrNbCallback delegateMgrCallback[STORE_CNT]; + + function Nbfunction[STORE_CNT] = { + bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback[INDEX_ZEROTH], _1, _2), + bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback[INDEX_FIRST], _1, _2), + bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback[INDEX_SECOND], _1, _2), + bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback[INDEX_THIRD], _1, _2) + }; + SetDir(NB_DIRECTOR); + /** + * @tc.steps: step1. create NBdelegate(APP_ID_1, USER_ID_1) and set dataDir. + * @tc.expected: step1. Create success. + */ + manager[INDEX_ZEROTH] = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager[INDEX_ZEROTH], nullptr); + manager[INDEX_ZEROTH]->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + + /** + * @tc.steps: step2. create NBdelegate(APP_ID_1, USER_ID_2) and set dataDir. + * @tc.expected: step2. Create success. + */ + manager[INDEX_FIRST] = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_2); + ASSERT_NE(manager[INDEX_FIRST], nullptr); + manager[INDEX_FIRST]->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + + /** + * @tc.steps: step3. create NBdelegate(APP_ID_2, USER_ID_1) and set dataDir. + * @tc.expected: step3. Create success. + */ + manager[INDEX_SECOND] = new (std::nothrow) KvStoreDelegateManager(APP_ID_2, USER_ID_1); + ASSERT_NE(manager[INDEX_SECOND], nullptr); + manager[INDEX_SECOND]->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + /** + * @tc.steps: step4. create NBdelegate(APP_ID_2, USER_ID_2) and set dataDir. + * @tc.expected: step4. Create success. + */ + manager[INDEX_THIRD] = new (std::nothrow) KvStoreDelegateManager(APP_ID_2, USER_ID_2); + ASSERT_NE(manager[INDEX_THIRD], nullptr); + manager[INDEX_THIRD]->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }); + /** + * @tc.steps: step5. use STORE_ID_1 create db. + * @tc.expected: step5. Create success. + */ + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + manager[INDEX_ZEROTH]->GetKvStore(STORE_ID_1, option, Nbfunction[INDEX_ZEROTH]); + EXPECT_EQ(delegateMgrCallback[INDEX_ZEROTH].GetStatus(), OK); + callbackKvStore[INDEX_ZEROTH] = delegateMgrCallback[INDEX_ZEROTH].GetKvStore(); + EXPECT_EQ(manager[INDEX_ZEROTH]->CloseKvStore(callbackKvStore[INDEX_ZEROTH]), OK); + /** + * @tc.steps: step6. use STORE_ID_2 create db. + * @tc.expected: step6. Create success. + */ + manager[INDEX_FIRST]->GetKvStore(STORE_ID_2, option, Nbfunction[INDEX_FIRST]); + EXPECT_EQ(delegateMgrCallback[INDEX_FIRST].GetStatus(), OK); + callbackKvStore[INDEX_FIRST] = delegateMgrCallback[INDEX_FIRST].GetKvStore(); + EXPECT_EQ(manager[INDEX_FIRST]->CloseKvStore(callbackKvStore[INDEX_FIRST]), OK); + /** + * @tc.steps: step7. use STORE_ID_3 create db. + * @tc.expected: step7. Create success. + */ + manager[INDEX_SECOND]->GetKvStore(STORE_ID_3, option, Nbfunction[INDEX_SECOND]); + EXPECT_EQ(delegateMgrCallback[INDEX_SECOND].GetStatus(), OK); + callbackKvStore[INDEX_SECOND] = delegateMgrCallback[INDEX_SECOND].GetKvStore(); + EXPECT_EQ(manager[INDEX_SECOND]->CloseKvStore(callbackKvStore[INDEX_SECOND]), OK); + /** + * @tc.steps: step8. use STORE_ID_4 create db. + * @tc.expected: step8. Create success. + */ + manager[INDEX_THIRD]->GetKvStore(STORE_ID_4, option, Nbfunction[INDEX_THIRD]); + EXPECT_EQ(delegateMgrCallback[INDEX_THIRD].GetStatus(), OK); + callbackKvStore[INDEX_THIRD] = delegateMgrCallback[INDEX_THIRD].GetKvStore(); + EXPECT_EQ(manager[INDEX_THIRD]->CloseKvStore(callbackKvStore[INDEX_THIRD]), OK); + + for (unsigned int index = 0; index < STORE_CNT; index++) { + EXPECT_EQ(manager[index]->DeleteKvStore(storeId[index]), OK); + delete manager[index]; + manager[index] = nullptr; + } +} + +/* + * @tc.name: ManagerDb 011 + * @tc.desc: verify whether it will check if storeid is invalid. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb011, TestSize.Level1) +{ + /** + * @tc.steps: step1. Construct valid storeId, length equals to 128 characters, and which contains '\0', + * up letter and low letter, invalid storeId which include null, or contains '\\' '//' '&' '^' '%' '#', + * '..', '……', chinese, latins or longer than 128 characters. + * @tc.expected: step1. Construct success. + */ + KvStoreDelegateManager *manager = nullptr; + const std::string STORE_OK = + "STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE"\ + "_OK1_STORE_OK1_STORE_OK"; + const std::string STORE_ERROR = + "STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_"\ + "ERR_STORE_ERR_STORE_ERR"; + const std::string STORE_ID[ID_MAX_CNT] = { "", "STORE_ID_1\0", STORE_OK, STORE_ERROR, "Store_ID_2", + "STORE_ID_1\\", "STORE_ID_1//", "STORE_I&D_1", "STORE_ID_1^", "STORE_ID%_1", + "STORE#_ID_1", "STORE_I+D_1", "STORE_拜拜", "STOREÄäÖöÜü_ID_", " b", "store~Id" }; + DistributedDB::DBStatus resultStatus[ID_MAX_CNT] = { + INVALID_ARGS, OK, OK, INVALID_ARGS, OK, + INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, + INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS }; + SetDir(CONFIG.dataDir); + + /** + * @tc.steps: step2. Use different storeid create db. + * @tc.expected: step2. return ok if storeid is valid, and return INVALID_ARGS if storeId is invalid. + */ + DelegateMgrNbCallback delegateMgrNbCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrNbCallback, _1, _2); + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + manager->SetKvStoreConfig(CONFIG); + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + for (unsigned int index = 0; index < ID_MAX_CNT; index++) { + MST_LOG("index: %d", index); + manager->GetKvStore(STORE_ID[index], option, Nbfunction); + EXPECT_EQ(delegateMgrNbCallback.GetStatus(), resultStatus[index]); + KvStoreNbDelegate *delegate = const_cast(delegateMgrNbCallback.GetKvStore()); + EXPECT_EQ(manager->CloseKvStore(delegate), resultStatus[index]); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID[index]), resultStatus[index]); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 013 + * @tc.desc: verify that open db with IS_NEED_CREATE and IS_NOT_NEED_CREATE mode. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb013, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + /** + * @tc.steps: step1. open db with IS_NOT_NEED_CREATE mode . + * @tc.expected: step1. open failed. + */ + Option option = g_option; + option.createIfNecessary = IS_NOT_NEED_CREATE; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_EQ(manager, nullptr); + ASSERT_EQ(result, nullptr); + + /** + * @tc.steps: step2. open db with IS_NEED_CREATE mode . + * @tc.expected: step2. open success. + */ + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, g_option.isMemoryDb)); +} + +void GetAndCloseKvStore(KvStoreDelegateManager *&manager, const std::string &storeId, + KvStoreNbDelegate::Option &option, function &nbfunction, + DelegateMgrNbCallback &delegateMgrCallback) +{ + manager->GetKvStore(storeId, option, nbfunction); + EXPECT_EQ(delegateMgrCallback.GetStatus(), OK); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; +} +/* + * @tc.name: ManagerDb 014 + * @tc.desc: verify that open db reduplicatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb014, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + SetDir(CONFIG.dataDir); + EXPECT_EQ(manager->SetKvStoreConfig(CONFIG), OK); + + KvStoreNbDelegate::Option option; + for (int cnt = 0; cnt < MANYTINES; cnt++) { + /** + * @tc.steps: step1. open db with IS_NEED_CREATE mode. + * @tc.expected: step1. open success. + */ + option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + GetAndCloseKvStore(manager, STORE_ID_1, option, Nbfunction, delegateMgrCallback); + + /** + * @tc.steps: step2. open db with IS_NOT_NEED_CREATE mode. + * @tc.expected: step2. open success. + */ + option.createIfNecessary = IS_NOT_NEED_CREATE; + GetAndCloseKvStore(manager, STORE_ID_1, option, Nbfunction, delegateMgrCallback); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 015 + * @tc.desc: verify that check storeid when open db. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb015, TestSize.Level1) +{ + /** + * @tc.steps: step1. Construct valid storeId, length equals to 128 characters, and which contains '\0', + * up letter and low letter, invalid storeId which include null, or contains '\\' '//' '&' '^' '%' '#', + * '..', '……', chinese, latins or longer than 128 characters. + * @tc.expected: step1. Construct success. + */ + const std::string STORE_OK = + "STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1_STORE_OK1"\ + "_STORE_OK1_STORE_OK1_STORE_OK"; + const std::string STORE_ERROR = + "STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR_STORE_ERR"\ + "_STORE_ERR_STORE_ERR_STORE_ERR"; + const std::string STORE_ID[ID_MAX_CNT] = { "", "STORE_ID_1\0", STORE_OK, STORE_ERROR, "Store_ID_2", + "STORE_ID_1\\", "STORE_ID_1//", "STORE_ID_1&", "STORE_ID_1^", "STORE_%ID_1", + "STORE_ID_1#", "STORE_I-D_1", "STORE_编号", "STORE_ID_ÄäÖöÜü", " b", "STORE_ID_1" }; + DistributedDB::DBStatus resultStatus[ID_MAX_CNT] = { + INVALID_ARGS, OK, OK, INVALID_ARGS, OK, + INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, + INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, INVALID_ARGS, OK }; + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_NE(delegate, nullptr); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step2. Use different storeid open and create db. + * @tc.expected: step2. return ok if storeid is valid, and return INVALID_ARGS if storeId is invalid. + */ + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + for (unsigned int index = 0; index < ID_MAX_CNT; index++) { + MST_LOG("ManagerDb015 open %d db.", index); + manager->GetKvStore(STORE_ID[index], option, Nbfunction); + EXPECT_EQ(delegateMgrCallback.GetStatus(), resultStatus[index]); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_EQ(manager->CloseKvStore(delegate), resultStatus[index]); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID[index]), resultStatus[index]); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 018 + * @tc.desc: verify that can close db normally. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb018, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. close db normally. + * @tc.expected: step1. success. + */ + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, g_option.isMemoryDb)); +} + +/* + * @tc.name: ManagerDb 019 + * @tc.desc: verify that can't close db that do not exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb019, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + /** + * @tc.steps: step1. close the db that is not exist. + * @tc.expected: step1. close failed. + */ + EXPECT_EQ(manager->CloseKvStore(result), INVALID_ARGS); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 020 + * @tc.desc: verify that can't close db that is closed. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb020, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + SetDir(NB_DIRECTOR); + manager->SetKvStoreConfig(CONFIG); + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + manager->GetKvStore(STORE_ID_1, option, Nbfunction); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_NE(delegate, nullptr); + /** + * @tc.steps: step1. make sure that db is closed. + * @tc.expected: step1. close success. + */ + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + /** + * @tc.steps: step2. close it again. + * @tc.expected: step2. close failed. + */ + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 023 + * @tc.desc: verify that delete the handler of the db. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb023, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. close db and delete db, then check the db document manually. + * @tc.expected: step1. close and delete db successfully, and the document is not exist. + */ + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, g_option.isMemoryDb)); +} + +/* + * @tc.name: ManagerDb 024 + * @tc.desc: verify that delete db that was not closed. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb024, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + Option option = g_option; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. delete the db that was not closed and check db document manually. + * @tc.expected: step1. delete failed and db document is still exist + */ + if (!option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), BUSY); + } + + EXPECT_TRUE(EndCaseDeleteDB(manager, result, STORE_ID_1, option.isMemoryDb)); +} + +/* + * @tc.name: ManagerDb 025 + * @tc.desc: verify that can delete db that was not open. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb025, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + /** + * @tc.steps: step1. delete the db that was not open and check db document manually. + * @tc.expected: step1. delete success and db document is deleted. + */ + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 026 + * @tc.desc: verify that can't delete db that was not exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb026, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. delete the storeid that is not exist and check document manually. + * @tc.expected: step1. return failed and db document has not changes. + */ + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), NOT_FOUND); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 027 + * @tc.desc: verify that can't delete db reduplicatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb027, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. close the db. + * @tc.expected: step1. close success. + */ + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + /** + * @tc.steps: step2. delete the db first time and check db document manually. + * @tc.expected: step2. delete success and db document was deleted. + */ + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + /** + * @tc.steps: step2. delete the db the second time. + * @tc.expected: step2. delete failed and db document has no changes. + */ + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), NOT_FOUND); + } + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 028 + * @tc.desc: verify that can't open db that was deleted. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb028, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction1 + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. close the db normally. + * @tc.expected: step1. close success. + */ + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + /** + * @tc.steps: step2. delete the db normally. + * @tc.expected: step2. delete success. + */ + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + /** + * @tc.steps: step3. open the db that was deleted. + * @tc.expected: step3. opened failed. + */ + KvStoreNbDelegate::Option option = DistributedDBNbTestTools::TransferNbOptionType(g_option); + option.createIfNecessary = IS_NOT_NEED_CREATE; + manager->GetKvStore(STORE_ID_1, option, Nbfunction1); + EXPECT_NE(delegateMgrCallback.GetStatus(), OK); + KvStoreNbDelegate *delegate = const_cast(delegateMgrCallback.GetKvStore()); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), INVALID_ARGS); + result = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), NOT_FOUND); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 029 + * @tc.desc: verify that can create db again after it was deleted. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb029, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + DelegateMgrNbCallback delegateMgrCallback; + function Nbfunction + = bind(&DelegateMgrNbCallback::Callback, &delegateMgrCallback, _1, _2); + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step1. close db. + * @tc.expected: step1. close success. + */ + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + /** + * @tc.steps: step2. delete db . + * @tc.expected: step2. delete success. + */ + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + delete manager; + manager = nullptr; + /** + * @tc.steps: step2. create db after it is delete. + * @tc.expected: step2. create success. + */ + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ManagerDb 030 + * @tc.desc: verify that can delete the db success even if the deleting process is killed when it is deleting the db. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCreateTest, ManagerDb030, TestSize.Level2) +{ + /** + * @tc.steps: step1. start thread. + * @tc.expected: step1. success. + */ + std::thread th(WaitingDeletingDB); + th.detach(); + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND)); +} + +/* + * @tc.name: MemoryDb 001 + * @tc.desc: verify that can create memory db successfully. + * @tc.type: FUNC + * @tc.require: SR000CRAD8 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, MemoryDb001, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. create memory db with isMemoryDb=true in SetKvStoreConfig. + * @tc.expected: step1. create successfully. + */ + Option option; + option.createIfNecessary = IS_NEED_CREATE; + option.isMemoryDb = true; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + fstream dbFile; + dbFile.open((NB_DIRECTOR + "single_ver/main/gen_natural_store.*"), ios::in); + if (!dbFile) { + MST_LOG("The db file is not exist!"); + } else { + dbFile.close(); + } + + /** + * @tc.steps: step2. PutLocal(k1,v1) to MemoryDb. + * @tc.expected: step2. operate successfully and GetLocal(k1)=v1. + */ + EXPECT_EQ(result->PutLocal(KEY_1, VALUE_1), OK); + Value valueResult; + EXPECT_EQ(result->GetLocal(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(result->DeleteLocal(KEY_1), OK); + EXPECT_EQ(result->GetLocal(KEY_1, valueResult), NOT_FOUND); + + /** + * @tc.steps: step3. Put(k1,v1) to MemoryDb. + * @tc.expected: step3. operate successfully and Get(k1)=v1. + */ + EXPECT_EQ(result->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(result->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(result->Delete(KEY_1), OK); + EXPECT_EQ(result->Get(KEY_1, valueResult), NOT_FOUND); + + /** + * @tc.steps: step4. delete MemoryDb. + * @tc.expected: step4. delete failed. + */ + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), BUSY); + + /** + * @tc.steps: step5. close MemoryDb. + * @tc.expected: step5. close successfully. + */ + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + delete manager; + manager = nullptr; +} + +void ReleaseManager(KvStoreDelegateManager *&manager) +{ + if (manager != nullptr) { + delete manager; + manager = nullptr; + } + return; +} +/* + * @tc.name: MemoryDb 002 + * @tc.desc: verify that the differently configured databases(e.g. diskdb,memdb) won't affect each other. + * @tc.type: FUNC + * @tc.require: SR000CRAD8 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, MemoryDb002, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result1 = nullptr, *result2 = nullptr; + + /** + * @tc.steps: step1. Open diskdb and put(k1,v1),putlocal(k1,v2) to diskdb. Then create memorydb with the same 3- + * params of opened diskdb. + * @tc.expected: step1. open the diskdb successfully and failed to open the corresponding memdb. + */ + Option option; + option.createIfNecessary = IS_NEED_CREATE; + option.isMemoryDb = false; + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + EXPECT_EQ(result1->PutLocal(KEY_1, VALUE_2), OK); + EXPECT_EQ(result1->Put(KEY_1, VALUE_1), OK); + option.isMemoryDb = true; + KvStoreDelegateManager *managerRes = nullptr; + ASSERT_EQ(DistributedDBNbTestTools::GetNbDelegateSuccess(managerRes, g_dbParameter1, option), nullptr); + DistributedDBNbTestTools::CloseNbAndRelease(manager, result1); + + /** + * @tc.steps: step2. device A put(k2,v3),putlocal(k2,v2) to memory db and open disk db then close memory db. + * @tc.expected: step2. put successfully and open disk db failed. + */ + option.isMemoryDb = true; + result2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result2 != nullptr); + EXPECT_EQ(result2->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(result2->Put(KEY_2, VALUE_3), OK); + option.isMemoryDb = false; + ASSERT_EQ(DistributedDBNbTestTools::GetNbDelegateSuccess(managerRes, g_dbParameter1, option), nullptr); + DistributedDBNbTestTools::CloseNbAndRelease(manager, result2); + + /** + * @tc.steps: step3. device A Get(k1),Get(k2),GetLocal(k1),GetLocal(k2) in disk db. + * @tc.expected: step3. Get(k1)=v1, Get(k2)=NOT_FOUND, GetLocal(k1)=v2, GetLocal(k2)=NOT_FOUND. + */ + option.isMemoryDb = false; + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + Value valueResult; + EXPECT_EQ(result1->GetLocal(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(result1->GetLocal(KEY_2, valueResult), NOT_FOUND); + EXPECT_EQ(result1->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(result1->Get(KEY_2, valueResult), NOT_FOUND); + DistributedDBNbTestTools::CloseNbAndRelease(manager, result1); + /** + * @tc.steps: step4. device A Get(k1),Get(k2),GetLocal(k1),GetLocal(k2) in memory db. + * @tc.expected: step4. Get(k1)=NOT_FOUND, Get(k2)=NOT_FOUND, GetLocal(k1)=NOT_FOUND, GetLocal(k2)=NOT_FOUND. + */ + option.isMemoryDb = true; + result2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result2 != nullptr); + EXPECT_EQ(result2->GetLocal(KEY_1, valueResult), NOT_FOUND); + EXPECT_EQ(result2->GetLocal(KEY_2, valueResult), NOT_FOUND); + EXPECT_EQ(result2->Get(KEY_1, valueResult), NOT_FOUND); + EXPECT_EQ(result2->Get(KEY_2, valueResult), NOT_FOUND); + EXPECT_EQ(manager->CloseKvStore(result2), OK); + result2 = nullptr; + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + result1 = nullptr; + ReleaseManager(manager); +} + +/* + * @tc.name: OptionParam 001 + * @tc.desc: verify that will check the option parameter when create encrypted DB. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, OptionParam001, TestSize.Level0) +{ + vector manager = {nullptr, nullptr, nullptr}; + vector result = {nullptr, nullptr, nullptr, nullptr, nullptr}; + Option option; + vector password; + + /** + * @tc.steps: step1. isEncryptedDb=true, passwd=NULL, cipher=DEFAULT when create db. + * @tc.expected: step1. create failed and return INVALID_ARGS. + */ + option.isEncryptedDb = true; + option.cipher = CipherType::DEFAULT; + option.passwd = NULL_PASSWD_VECTOR; + DBStatus status; + result[INDEX_ZEROTH] = DistributedDBNbTestTools::GetNbDelegateStatus(manager[INDEX_ZEROTH], + status, g_dbParameter1, option); + EXPECT_EQ(result[INDEX_ZEROTH], nullptr); + EXPECT_EQ(status, INVALID_ARGS); + + /** + * @tc.steps: step2. isEncryptedDb=true, passwd=a......(100B) when create db. + * @tc.expected: step2. create successfully and return OK. + */ + password.assign(VALUE_ONE_HUNDRED_BYTE, 'a'); + option.passwd = password; + result[INDEX_FIRST] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_ZEROTH], g_dbParameter1, option); + ASSERT_TRUE(manager[INDEX_ZEROTH] != nullptr && result[INDEX_FIRST] != nullptr); + EXPECT_EQ(manager[INDEX_ZEROTH]->CloseKvStore(result[INDEX_FIRST]), OK); + result[INDEX_FIRST] = nullptr; + + /** + * @tc.steps: step3. isEncryptedDb=true, passwd=a......(128B), cipher=AES_256_GCM when create db. + * @tc.expected: step3. create successfully and return OK. + */ + password.clear(); + password.assign(BATCH_RECORDS, 'a'); + option.cipher = CipherType::AES_256_GCM; + option.passwd = password; + result[INDEX_SECOND] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_FIRST], g_dbParameter2, option); + ASSERT_TRUE(manager[INDEX_FIRST] != nullptr && result[INDEX_SECOND] != nullptr); + EXPECT_EQ(manager[INDEX_FIRST]->CloseKvStore(result[INDEX_SECOND]), OK); + result[INDEX_SECOND] = nullptr; + + /** + * @tc.steps: step4. isEncryptedDb=true, passwd=a......(129B), cipher=DEFAULT when create db. + * @tc.expected: step4. create failed and return INVALID_ARGS. + */ + password.clear(); + password.assign(PASSWD_BYTE, 'a'); + option.cipher = CipherType::DEFAULT; + option.passwd = password; + result[INDEX_THIRD] = DistributedDBNbTestTools::GetNbDelegateStatus(manager[INDEX_SECOND], + status, g_dbParameter3, option); + EXPECT_EQ(result[INDEX_THIRD], nullptr); + EXPECT_EQ(status, INVALID_ARGS); + + /** + * @tc.steps: step5. isEncryptedDb=false, passwd=a......(129B), cipher=DEFAULT when create db. + * @tc.expected: step5. create successfully and return OK. + */ + option.isEncryptedDb = false; + result[INDEX_FORTH] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_SECOND], g_dbParameter3, option); + ASSERT_TRUE(manager[INDEX_SECOND] != nullptr && result[INDEX_FORTH] != nullptr); + EXPECT_EQ(manager[INDEX_SECOND]->CloseKvStore(result[INDEX_FORTH]), OK); + result[INDEX_FORTH] = nullptr; + + EXPECT_EQ(manager[INDEX_ZEROTH]->DeleteKvStore(STORE_ID_1), OK); + EXPECT_EQ(manager[INDEX_FIRST]->DeleteKvStore(STORE_ID_2), OK); + EXPECT_EQ(manager[INDEX_SECOND]->DeleteKvStore(STORE_ID_3), OK); + for (auto &item : manager) { + if (item != nullptr) { + delete item; + } + } +} + +/* + * @tc.name: OptionParam 002 + * @tc.desc: verify that will memDb can't be encrypted. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, OptionParam002, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + Option option; + vector password; + + /** + * @tc.steps: step1. isEncryptedDb=true, passwd=a......(100B), cipher=DEFAULT when create memdb. + * @tc.expected: step1. create failed and return NOT_SUPPORT. + */ + option.isEncryptedDb = true; + option.isMemoryDb = true; + option.cipher = CipherType::DEFAULT; + password.assign(VALUE_ONE_HUNDRED_BYTE, 'a'); + option.passwd = password; + DBStatus status; + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, option); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, NOT_SUPPORT); + + /** + * @tc.steps: step2. isEncryptedDb=false, passwd=a......(100B) when create memdb. + * @tc.expected: step2. create successfully and return OK. + */ + option.isEncryptedDb = false; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + delete manager; + manager = nullptr; +} + +/* + * @tc.name: OptionParam 003 + * @tc.desc: verify that isEncryptedDb and passwd are consistent with the creation time can open an existing DB. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, OptionParam003, TestSize.Level0) +{ + vectormanager = {nullptr, nullptr, nullptr, nullptr}; + KvStoreNbDelegate *result = nullptr; + + /** + * @tc.steps: step1. isEncryptedDb=true, passwd=p1, cipher=DEFAULT when create db1. + * @tc.expected: step1. create successfully and return OK. + */ + Option option1 = {true, false, true, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_1}; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_ZEROTH], g_dbParameter1, option1); + ASSERT_TRUE(manager[INDEX_ZEROTH] != nullptr && result != nullptr); + EXPECT_EQ(manager[INDEX_ZEROTH]->CloseKvStore(result), OK); + result = nullptr; + /** + * @tc.steps: step2. isEncryptedDb=false, passwd=p1 when open db1. + * @tc.expected: step2. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + Option option2 = {false, false, false, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_1}; + DBStatus status; + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager[INDEX_FIRST], status, g_dbParameter1, option2); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. isEncryptedDb=true, passwd=p2 when open db1. + * @tc.expected: step3. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + Option option3 = {false, false, true, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_2}; + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager[INDEX_FIRST], status, g_dbParameter1, option3); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step4. isEncryptedDb=true, passwd=p1 when open db1. + * @tc.expected: step4. open successfully and return OK. + */ + Option option4 = {false, false, true, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_1}; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_FIRST], g_dbParameter1, option4); + ASSERT_TRUE(manager[INDEX_FIRST] != nullptr && result != nullptr); + EXPECT_EQ(manager[INDEX_FIRST]->CloseKvStore(result), OK); + result = nullptr; + + /** + * @tc.steps: step5. isEncryptedDb=false, passwd=p1, cipher=DEFAULT when create db2. + * @tc.expected: step5. create successfully and return OK. + */ + Option option5 = {true, false, false, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_1}; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_SECOND], g_dbParameter2, option5); + ASSERT_TRUE(manager[INDEX_SECOND] != nullptr && result != nullptr); + EXPECT_EQ(manager[INDEX_SECOND]->CloseKvStore(result), OK); + result = nullptr; + + /** + * @tc.steps: step6. isEncryptedDb=true, passwd=p1 when open db2. + * @tc.expected: step6. open failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + Option option6 = {false, false, true, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_1}; + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager[INDEX_THIRD], status, g_dbParameter2, option6); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step7. isEncryptedDb=false, passwd=p2 when open db2. + * @tc.expected: step7. open successfully and return OK. + */ + Option option7 = {false, false, false, DistributedDB::CipherType::DEFAULT, PASSWD_VECTOR_2}; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager[INDEX_THIRD], g_dbParameter2, option7); + ASSERT_TRUE(manager[INDEX_THIRD] != nullptr && result != nullptr); + EXPECT_EQ(manager[INDEX_THIRD]->CloseKvStore(result), OK); + result = nullptr; + EXPECT_EQ(manager[INDEX_FIRST]->DeleteKvStore(STORE_ID_1), OK); + EXPECT_EQ(manager[INDEX_THIRD]->DeleteKvStore(STORE_ID_2), OK); + for (auto &item : manager) { + if (item != nullptr) { + delete item; + item = nullptr; + } + } +} + +void RekeyAndClose(KvStoreDelegateManager *&manager, KvStoreNbDelegate *&delegate, + const DistributedDB::CipherPassword &reKeyPasswd, DistributedDB::DBStatus rekeyStatus, bool isEnd) +{ + EXPECT_EQ(delegate->Rekey(reKeyPasswd), rekeyStatus); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + if (!isEnd) { + ReleaseManager(manager); + } +} + +/* + * @tc.name: RekeyDb 001 + * @tc.desc: verify that can switching a non-encrypted database to an encrypted database by Rekey. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, RekeyDb001, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + Option option; + + /** + * @tc.steps: step1. use Rekey to update passwd to p1 after create memdb and then close memdb. + * @tc.expected: step1. the Rekey return NOT_SUPPORT. + */ + option.isMemoryDb = true; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + RekeyAndClose(manager, result, g_passwd1, NOT_SUPPORT, false); + /** + * @tc.steps: step2. create unencrypted db, use Rekey to update its passwd to NULL, + * then close and open without passwd. + * @tc.expected: step2. operate successfully and can open db again without passwd. + */ + option.isMemoryDb = false; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + RekeyAndClose(manager, result, NULL_PASSWD, OK, false); + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(result->PutLocal(KEY_1, VALUE_1), OK); + EXPECT_EQ(result->Put(KEY_1, VALUE_1), OK); + + /** + * @tc.steps: step3. use Rekey to update db's passwd to p1=a......(100B), then close + * and open again without passwd. + * @tc.expected: step3. Rekey is ok, open db failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + vector passwordVector(VALUE_ONE_HUNDRED_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + RekeyAndClose(manager, result, password, OK, false); + DBStatus status; + EXPECT_EQ(DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, option), nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step4. use p1 to open db and Get(k1), GetLocal(k1). + * @tc.expected: step4. open successfully and Get(k1)=v1, GetLocal(k1)=v1. + */ + option.isEncryptedDb = true; + option.passwd = passwordVector; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + Value valueResult; + EXPECT_EQ(result->GetLocal(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(result->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + + /** + * @tc.steps: step5. use Rekey to update db's passwd to p2=passwordVector whose size is 128B, then close + * and open again with p1. + * @tc.expected: step5. Rekey is ok, open db failed and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + passwordVector.assign(BATCH_RECORDS, 'a'); + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OK); + RekeyAndClose(manager, result, password, OK, false); + EXPECT_EQ(DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, option), nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step6. use p2 to open db and delete(k1), DeleteLocal(k1). + * @tc.expected: step6. operate successfully. + */ + option.passwd = passwordVector; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(result->Delete(KEY_1), OK); + EXPECT_EQ(result->DeleteLocal(KEY_1), OK); + + /** + * @tc.steps: step7. use Rekey to update db's passwd to p2=passwordVector whose size is 129B. + * @tc.expected: step7. Rekey failed and return INVALID_ARGS. + */ + passwordVector.assign(PASSWD_BYTE, 'a'); + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} + +/* + * @tc.name: RekeyDb 002 + * @tc.desc: verify that can change passwd of an encrypted database by Rekey. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, RekeyDb002, TestSize.Level1) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + Option option; + + /** + * @tc.steps: step1. create encrypted db with p1=password@123. + * @tc.expected: step1. create successfully. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + + /** + * @tc.steps: step2. use Rekey to update passwd to p1 and close db then open db again with unencrypted way. + * @tc.expected: step2. the Rekey return OK, but open db failed. + */ + EXPECT_EQ(result->Rekey(g_passwd1), OK); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + ReleaseManager(manager); + option.createIfNecessary = false; + option.isEncryptedDb = false; + DBStatus status; + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, option); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. open db with passwd=p1 and put(k1,v1), putLocal(k2,v2). + * @tc.expected: step3. operate successfully. + */ + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(result->PutLocal(KEY_2, VALUE_2), OK); + EXPECT_EQ(result->Put(KEY_1, VALUE_1), OK); + + /** + * @tc.steps: step4. use Rekey to update passwd to NULL and close db then open db with passwd=p1. + * @tc.expected: step4. the Rekey return OK, but open db failed. + */ + EXPECT_EQ(result->Rekey(NULL_PASSWD), OK); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + ReleaseManager(manager); + result = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter1, option); + EXPECT_EQ(result, nullptr); + EXPECT_EQ(status, INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step5. open db again with unencrypted way and Get(k1), GetLocal(k2). + * @tc.expected: step5. open successfully and Get(k1)=v1, GetLocal(k2)=v2. + */ + option.isEncryptedDb = false; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + Value valueResult; + EXPECT_EQ(result->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_1); + EXPECT_EQ(result->GetLocal(KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + + /** + * @tc.steps: step6. use Rekey to update db's passwd to p2=a......(129B). + * @tc.expected: step6. Reksy failed and return INVALID_ARGS. + */ + vector passwordVector(PASSWD_BYTE, 'a'); + CipherPassword password; + EXPECT_EQ(password.SetValue(passwordVector.data(), passwordVector.size()), CipherPassword::ErrorCode::OVERSIZE); + + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: RekeyDb 003 + * @tc.desc: verify that do other operations during Rekey execution, the operation returns busy. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, RekeyDb003, TestSize.Level3) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result1 = nullptr; + KvStoreNbDelegate *result2 = nullptr; + Option option; + + /** + * @tc.steps: step1. use Rekey to update passwd to p1=PASSWD_VECTOR_1. + * @tc.expected: step1. create successfully. + */ + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, NB_OPERATION_NUM, ONE_K_LONG_STRING, ONE_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_EQ(result1->Put(iter->key, iter->value), OK); + } + bool rekeyFlag = false; + thread subThread([&]() { + DBStatus rekeyStatus = result1->Rekey(g_passwd1); + EXPECT_TRUE(rekeyStatus == OK || rekeyStatus == BUSY); + rekeyFlag = true; + g_conditionNbVar.notify_all(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. Call the GetKvstore interface when Rekey. + * @tc.expected: step2. the GetKvstore return BUSY. + */ + option.createIfNecessary = false; + DBStatus status; + KvStoreDelegateManager *managerRes = nullptr; + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_LONG_TIME)); + result2 = DistributedDBNbTestTools::GetNbDelegateStatus(managerRes, status, g_dbParameter1, option); + EXPECT_TRUE(status == BUSY || status == OK); + if (status == OK) { + EXPECT_EQ(managerRes->CloseKvStore(result2), OK); + result2 = nullptr; + ReleaseManager(managerRes); + } + + /** + * @tc.steps: step3. put data to db when Rekey. + * @tc.expected: step3. the put return BUSY. + */ + status = result1->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == BUSY || status == OK); + std::mutex count; + std::unique_lock lck(count); + g_conditionNbVar.wait(lck, [&] { return rekeyFlag; }); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + result1 = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} +#endif + +/* + * @tc.name: RekeyDb 004 + * @tc.desc: verify that Rekey will return busy when there are multiple instances of the same KvStore. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, RekeyDb004, TestSize.Level0) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result1 = nullptr; + KvStoreNbDelegate *result2 = nullptr; + Option option; + + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + ReleaseManager(manager); + /** + * @tc.steps: step1. use GetKvstore to open another instances of the same KvStore. + * @tc.expected: step1. open successfully. + */ + option.createIfNecessary = false; + result2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result2 != nullptr); + + /** + * @tc.steps: step2. call Rekey. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_EQ(result1->Rekey(g_passwd1), BUSY); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + result1 = nullptr; + EXPECT_EQ(manager->CloseKvStore(result2), OK); + result2 = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); +} + +void RunDbRekeyOne() +{ + KvStoreDelegateManager *manager1 = nullptr; + KvStoreNbDelegate *result1 = nullptr; + Option option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter1, option); + ASSERT_TRUE(manager1 != nullptr && result1 != nullptr); + EXPECT_EQ(result1->Rekey(g_passwd2), OK); + EXPECT_EQ(manager1->CloseKvStore(result1), OK); + result1 = nullptr; + EXPECT_EQ(manager1->DeleteKvStore(STORE_ID_1), OK); + delete manager1; + manager1 = nullptr; +} + +void RunDbRekeyTwo() +{ + KvStoreDelegateManager *manager2 = nullptr; + KvStoreNbDelegate *result2 = nullptr; + Option option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_2; + result2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, option); + ASSERT_TRUE(manager2 != nullptr && result2 != nullptr); + EXPECT_EQ(result2->Rekey(g_passwd1), OK); + EXPECT_EQ(manager2->CloseKvStore(result2), OK); + result2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} + +void RunDbRekeyThree() +{ + KvStoreDelegateManager *manager3 = nullptr; + KvStoreNbDelegate *result3 = nullptr; + Option option; + option.isEncryptedDb = true; + option.passwd = PASSWD_VECTOR_1; + result3 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager3, g_dbParameter3, option); + ASSERT_TRUE(manager3 != nullptr && result3 != nullptr); + EXPECT_EQ(result3->Rekey(NULL_PASSWD), OK); + EXPECT_EQ(manager3->CloseKvStore(result3), OK); + result3 = nullptr; + EXPECT_EQ(manager3->DeleteKvStore(STORE_ID_3), OK); + delete manager3; + manager3 = nullptr; +} + +void RunDbRekeyFour() +{ + KvStoreDelegateManager *manager4 = nullptr; + KvStoreNbDelegate *result4 = nullptr; + Option option; + result4 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager4, g_dbParameter4, option); + ASSERT_TRUE(manager4 != nullptr && result4 != nullptr); + EXPECT_EQ(result4->Rekey(g_passwd1), OK); + EXPECT_EQ(manager4->CloseKvStore(result4), OK); + result4 = nullptr; + EXPECT_EQ(manager4->DeleteKvStore(STORE_ID_4), OK); + delete manager4; + manager4 = nullptr; +} + +void RunDbRekeyFive() +{ + KvStoreDelegateManager *manager5 = nullptr; + KvStoreNbDelegate *result5 = nullptr; + Option option; + result5 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager5, g_dbParameter5, option); + ASSERT_TRUE(manager5 != nullptr && result5 != nullptr); + EXPECT_EQ(result5->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(result5->Delete(KEY_1), OK); + EXPECT_EQ(result5->Put(KEY_1, VALUE_2), OK); + Value valueResult; + EXPECT_EQ(result5->Get(KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(manager5->CloseKvStore(result5), OK); + result5 = nullptr; + EXPECT_EQ(manager5->DeleteKvStore(STORE_ID_5), OK); + delete manager5; + manager5 = nullptr; +} +/* + * @tc.name: RekeyDb 005 + * @tc.desc: verify that calling Rekey interfaces on different DBs does not affect each other. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, RekeyDb005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create thread1 to create db1 with passwd=p1, call Rekey to update passwd to p2=PASSSWD_2. + * @tc.expected: step1. operate successfully. + */ + thread subThread1(RunDbRekeyOne); + + /** + * @tc.steps: step2. create thread2 to create db2 with passwd=p2, call Rekey to update passwd to p1=PASSSWD_1. + * @tc.expected: step2. operate successfully. + */ + thread subThread2(RunDbRekeyTwo); + + /** + * @tc.steps: step3. create thread3 to create db3 with passwd=p1, call Rekey to update passwd to NULL_PASSWD. + * @tc.expected: step3. operate successfully. + */ + thread subThread3(RunDbRekeyThree); + + /** + * @tc.steps: step4. create thread4 to create db4 without passwd, call Rekey to make db to be encrypted. + * @tc.expected: step4. operate successfully. + */ + thread subThread4(RunDbRekeyFour); + + /** + * @tc.steps: step5. create thread5 to create db5 without passwd, then CRUD data to db5. + * @tc.expected: step5. operate successfully. + */ + thread subThread5(RunDbRekeyFive); + subThread1.join(); + subThread2.join(); + subThread3.join(); + subThread4.join(); + subThread5.join(); +} + +/* + * @tc.name: SpaceManger 001 + * @tc.desc: verify that StoreID is empty or does not exist, calling the GetKvStoreDiskSize() interface failed. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, SpaceManger001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create DB1. + * @tc.expected: step1. create successfully. + */ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, g_option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + /** + * @tc.steps: step2. call the GetKvStoreDiskSize() with storeId=null. + * @tc.expected: step2. call failed and return INVALID_ARGS. + */ + uint64_t dbSize = 0ul; + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID, dbSize), INVALID_ARGS); + + /** + * @tc.steps: step3. call the GetKvStoreDiskSize() with storeId=store_Id_2. + * @tc.expected: step3. call failed and return NOT_FOUND. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_2, dbSize), NOT_FOUND); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + } + ReleaseManager(manager); +} + +/* + * @tc.name: SpaceManger 002 + * @tc.desc: verify that can calculate the space size normally with the existing databaseID. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCreateTest, SpaceManger002, TestSize.Level2) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *result = nullptr; + KvStoreNbDelegate *result1 = nullptr; + Option option; + result = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result != nullptr); + EXPECT_EQ(manager->CloseKvStore(result), OK); + result = nullptr; + + /** + * @tc.steps: step1. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step1. call successfully and return dbSize1. + */ + uint64_t dbSize1, dbSize2, dbSize3; + dbSize1 = dbSize2 = dbSize3 = 0ul; + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize1), OK); + ReleaseManager(manager); + + /** + * @tc.steps: step2. put 100 (keys,values) to db that every item's size = 1K. + * @tc.expected: step2. operate successfully. + */ + option.createIfNecessary = false; + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + + std::vector entriesBatch; + std::vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, OPER_CNT_END, ONE_K_LONG_STRING, FOUR_M_LONG_STRING); + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_EQ(result1->Put(iter->key, iter->value), OK); + } + + /** + * @tc.steps: step3. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step3. call successfully and return dbSize2, dbSize2 > dbsize1. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize2), OK); + EXPECT_TRUE(dbSize2 > dbSize1); + + /** + * @tc.steps: step4. delete the 100 (keys,values) that inserted in step2. + * @tc.expected: step4. operate successfully. + */ + for (vector::iterator iter = entriesBatch.begin(); iter != entriesBatch.end(); iter++) { + EXPECT_EQ(result1->Delete(iter->key), OK); + } + + /** + * @tc.steps: step5. call the GetKvStoreDiskSize() with storeId=store_Id_1. + * @tc.expected: step5. call successfully and return dbSize3, dbSize3 < dbsize2 and dbSize3 != dbsize2. + */ + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize3), OK); + EXPECT_TRUE(dbSize2 > dbSize3); + EXPECT_NE(dbSize3, dbSize1); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + result1 = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + ReleaseManager(manager); + + /** + * @tc.steps: step6. create memoryDb with storeId=store_Id_2 and call the GetKvStoreDiskSize(). + * @tc.expected: step6. call successfully and return dbSize1 and dbSize1=0. + */ + option.createIfNecessary = true; + option.isMemoryDb = true; + result1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && result1 != nullptr); + uint64_t dbSize4 = 0ul; + EXPECT_EQ(manager->GetKvStoreDiskSize(STORE_ID_1, dbSize4), OK); + EXPECT_EQ(dbSize4, size_t(0)); + EXPECT_EQ(manager->CloseKvStore(result1), OK); + result1 = nullptr; + ReleaseManager(manager); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_crud_test.cpp new file mode 100755 index 000000000..65184be48 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_crud_test.cpp @@ -0,0 +1,1961 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#include "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbCrud { +const unsigned int READ_RECORDS_NUM_START = 1; +const unsigned int READ_RECORDS_NUM_END = 1000; +const unsigned int WRITE_RECORDS_NUM_START = 1; +const unsigned int WRITE_RECORDS_NUM_END = 9999; +const unsigned int DELETE_RECORDS_NUM_START = 1; +const unsigned int DELETE_RECORDS_NUM_END = 1000; + +const unsigned long LONG_TIME_TEST_SECONDS = 10; +const unsigned long LONG_TIME_INTERVAL_MILLSECONDS = 5; + +const unsigned int RECORDS_NUM_THOUSAND = 1000; + +KvStoreNbDelegate *g_kvStoreNbDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; + +class DistributeddbNbCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbCrudTest::SetUpTestCase(void) +{ +} + +void DistributeddbNbCrudTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbCrudTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_kvStoreNbDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_kvStoreNbDelegate != nullptr); +} + +void DistributeddbNbCrudTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_kvStoreNbDelegate, STORE_ID_1, g_option.isMemoryDb)); + RemoveDir(NB_DIRECTOR); +} + +enum ReadOrWriteTag { + READ = 0, + WRITE = 1, + DELETE = 2 +}; + +struct ConcurParam { + unsigned int threadId_; + ReadOrWriteTag tag_; + Entry* entryPtr_; +}; +static std::mutex g_concurOperMutex; +// the thread runnnig methods +void ConcurOperThread(ConcurParam* args) +{ + auto paramsPtr = static_cast(args); + DBStatus status; + Value valueResult; + + if (paramsPtr->tag_ == READ) { + status = DistributedDBNbTestTools::Get(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key, valueResult); + if (valueResult.size() != 0) { + EXPECT_EQ(status, OK); + EXPECT_TRUE(DistributedDBNbTestTools::isValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } + } else if (paramsPtr->tag_ == WRITE) { + status = DistributedDBNbTestTools::Put(*g_kvStoreNbDelegate, + paramsPtr->entryPtr_->key, paramsPtr->entryPtr_->value); + ASSERT_EQ(status, DBStatus::OK); + + status = DistributedDBNbTestTools::Get(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key, valueResult); + EXPECT_EQ(status, DBStatus::OK); + EXPECT_TRUE(DistributedDBNbTestTools::isValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } else { + std::lock_guard lock(g_concurOperMutex); + status = DistributedDBNbTestTools::Get(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key, valueResult); + if (valueResult.size() != 0) { + status = DistributedDBNbTestTools::Delete(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key); + ASSERT_EQ(status, DBStatus::OK); + + valueResult = {}; + status = DistributedDBNbTestTools::Get(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key, valueResult); + EXPECT_EQ(status, DBStatus::NOT_FOUND); + EXPECT_EQ(valueResult.size(), (unsigned long)0); + } else { + status = DistributedDBNbTestTools::Delete(*g_kvStoreNbDelegate, paramsPtr->entryPtr_->key); + ASSERT_EQ(status, DBStatus::OK); + } + } +} + +double NbCalculateTime(ConcurParam *&nbThreadParam, const Entry entryCurrent, const SysTime &start, SysDurTime dur) +{ + SysTime end; + nbThreadParam->entryPtr_->key = entryCurrent.key; + nbThreadParam->entryPtr_->value = entryCurrent.value; + std::thread thread = std::thread(ConcurOperThread, reinterpret_cast(nbThreadParam)); + thread.join(); + + std::this_thread::sleep_for(std::chrono::duration(LONG_TIME_INTERVAL_MILLSECONDS)); + end = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + dur = std::chrono::duration_cast(end - start); + double operInterval = static_cast(dur.count()) * chrono::microseconds::period::num + / chrono::microseconds::period::den; + delete nbThreadParam->entryPtr_; + nbThreadParam->entryPtr_ = nullptr; + delete nbThreadParam; + nbThreadParam = nullptr; + return operInterval; +} + +/* + * @tc.name: GetStoreid 001 + * @tc.desc: Verify that can obtain storeid normally. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, GetStoreid001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create db and get storeId normally. + * @tc.expected: step1. success. + */ + EXPECT_EQ(g_kvStoreNbDelegate->GetStoreId(), STORE_ID_1); +} + +/* + * @tc.name: SimpleAction 001 + * @tc.desc: test that get data from local and sync db and they can't affect each other. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, SimpleAction001, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_3, VALUE_3); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. get value from local db when key = KEY_1. + * @tc.expected: step1. return value is VALUE_1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step2. get value from local db when key = KEY_2. + * @tc.expected: step2. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step3. get value from local db when key = KEY_3. + * @tc.expected: step3. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_3, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step4. get value from sync db when key = KEY_1. + * @tc.expected: step4. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step5. get value from sync db when key = KEY_2. + * @tc.expected: step5. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step6. get value from sync db when key = KEY_3. + * @tc.expected: step6. return value is VALUE_3. + */ + status = g_kvStoreNbDelegate->Get(KEY_3, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_3); + g_kvStoreNbDelegate->Delete(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); + g_kvStoreNbDelegate->Delete(KEY_3); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_2); +} + +/* + * @tc.name: SimpleAction 002 + * @tc.desc: test put data from local and sync db and they can't affect each other. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, SimpleAction002, TestSize.Level0) +{ + /** + * @tc.steps: step1. put (KEY_1, VALUE_1) to local db. + * @tc.expected: step1. put success. + */ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. put (KEY_2, VALUE_2) to local db. + * @tc.expected: step2. put success. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. put (KEY_1, VALUE_2) to sync db. + * @tc.expected: step3. put success. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. put (KEY_2, VALUE_1) to sync db. + * @tc.expected: step4. put success. + */ + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step6. get data from local db where key = KEY_1. + * @tc.expected: step6. return value is VALUE_1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step7. get data from local db where key = KEY_2. + * @tc.expected: step7. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step8. get data from sync db where key = KEY_1. + * @tc.expected: step8. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step9. get data from sync db where key = KEY_2. + * @tc.expected: step9. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + + g_kvStoreNbDelegate->Delete(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_2); +} + +/* + * @tc.name: SimpleAction 003 + * @tc.desc: test that update data to db, and local and sync db can't affect each other. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, SimpleAction003, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. update data (KEY_1, VALUE_2) to local db. + * @tc.expected: step1.update success. + */ + Value valueResult; + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. update data (KEY_2, VALUE_1) to local db. + * @tc.expected: step2.update success. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. update data (KEY_1, VALUE_1) to sync db. + * @tc.expected: step3.update success. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. update data (KEY_2, VALUE_2) to sync db. + * @tc.expected: step4.update success. + */ + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. get data from local db where key = KEY_1. + * @tc.expected: step5. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step6. get data from local db where key = KEY_2. + * @tc.expected: step6. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step7. get data from sync db where key = KEY_1. + * @tc.expected: step7. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step8. get data from sync db where key = KEY_2. + * @tc.expected: step8. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + + g_kvStoreNbDelegate->Delete(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_2); +} + +/* + * @tc.name: SimpleAction 004 + * @tc.desc: test that can delete entries of db, and local and sync db can't affect each other. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, SimpleAction004, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. delete entries from local db where key = KEY_1. + * @tc.expected: step1. delete success. + */ + status = g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. delete entries from sync db where key = KEY_2. + * @tc.expected: step2. delete success. + */ + status = g_kvStoreNbDelegate->Delete(KEY_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. get data from local where key = KEY_1. + * @tc.expected: step3. return value is NOT_FOUND. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step4. get data from local where key = KEY_2. + * @tc.expected: step4. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step5. get data from sync where key = KEY_1. + * @tc.expected: step5. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step6. get data from sync where key = KEY_2. + * @tc.expected: step6. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, NOT_FOUND); + + g_kvStoreNbDelegate->DeleteLocal(KEY_2); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: SimpleAction 005 + * @tc.desc: test that can query entries that don't exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, SimpleAction005, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. get data from local where key = KEY_2. + * @tc.expected: step1. return value is NOT_FOUND. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step2. get data from sync where key = KEY_1. + * @tc.expected: step2. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, NOT_FOUND); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); +} + +/* + * @tc.name: RepeatAction 001 + * @tc.desc: test that query the same entries repeatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction001, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. get data from local data where key = KEY_1. + * @tc.expected: step1. return value is VALUE_1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step2. get data from local data where key = KEY_1. + * @tc.expected: step2. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step3. get data from sync data where key = KEY_1. + * @tc.expected: step3. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step4. get data from sync data where key = KEY_1. + * @tc.expected: step4. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: RepeatAction 002 + * @tc.desc: test that put the same entries repeatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction002, TestSize.Level0) +{ + /** + * @tc.steps: step1. put (KEY_1, VALUE_1) to local db. + * @tc.expected: step1. put succeed. + */ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. put (KEY_1, VALUE_1) to local db again. + * @tc.expected: step2. put succeed. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. put (KEY_1, VALUE_2) to sync db. + * @tc.expected: step3. put succeed. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. put (KEY_1, VALUE_2) to sync db. + * @tc.expected: step4. put succeed. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step5. get data from local db where key = KEY_1. + * @tc.expected: step5. return value is VALUE_1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step6. get data from sync db where key = KEY_1. + * @tc.expected: step6. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: RepeatAction 003 + * @tc.desc: test that can update the same entries repeatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction003, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. update (KEY_1, VALUE_2) to local db. + * @tc.expected: step1. update success. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. update (KEY_1, VALUE_2) to local db again. + * @tc.expected: step2. update success. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. update (KEY_1, VALUE_1) to sync db. + * @tc.expected: step3. update success. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. update (KEY_1, VALUE_1) to sync db again. + * @tc.expected: step4. update success. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step5. get data from local where key KEY_1. + * @tc.expected: step5. return value is VALUE_2. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step6. get data from sync where key KEY_1. + * @tc.expected: step6. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: RepeatAction 004 + * @tc.desc: verify that it can delete the same entries repeatedly. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction004, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step1. delete KEY_1 from local db. + * @tc.expected: step1. delete succeed. + */ + status = g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. delete KEY_2 from sync db. + * @tc.expected: step2. delete succeed. + */ + status = g_kvStoreNbDelegate->Delete(KEY_2); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step3. delete KEY_1 from local db again. + * @tc.expected: step3. delete failed but return OK. + */ + status = g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step4. delete KEY_2 from sync db again. + * @tc.expected: step4. delete failed but return OK. + */ + status = g_kvStoreNbDelegate->Delete(KEY_2); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step5. get data from local db where key = KEY_1. + * @tc.expected: step5. return value is NOT_FOUND. + */ + Value realValue; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, realValue); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step6. get data from local db where key = KEY_2. + * @tc.expected: step6. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, realValue); + EXPECT_EQ(status, OK); + EXPECT_EQ(realValue, VALUE_2); + /** + * @tc.steps: step7. get data from sync db where key = KEY_1. + * @tc.expected: step7. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, realValue); + EXPECT_EQ(status, OK); + EXPECT_EQ(realValue, VALUE_2); + /** + * @tc.steps: step8. get data from sync db where key = KEY_2. + * @tc.expected: step8. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, realValue); + EXPECT_EQ(status, NOT_FOUND); + + g_kvStoreNbDelegate->DeleteLocal(KEY_2); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: RepeatAction 005 + * @tc.desc: test that delete the entries do not exist will return OK. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction005, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. delete KEY_2 from local db. + * @tc.expected: step1. delete failed but return OK. + */ + status = g_kvStoreNbDelegate->DeleteLocal(KEY_2); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. delete KEY_1 from sync db. + * @tc.expected: step2. delete failed but return OK. + */ + status = g_kvStoreNbDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step3. get data from local db where key = KEY_1. + * @tc.expected: step3. return value is VALUE_1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step4. get data from local db where key = KEY_2. + * @tc.expected: step4. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_2, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step5. get data from sync db where key = KEY_1. + * @tc.expected: step5. return value is NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step6. get data from sync db where key = KEY_2. + * @tc.expected: step6. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); +} + +/* + * @tc.name: RepeatAction 006 + * @tc.desc: test that can put entries which was deleted. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, RepeatAction006, TestSize.Level0) +{ + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. delete KEY_1 from local db. + * @tc.expected: step1. delete succeed. + */ + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. delete KEY_1 from sync db. + * @tc.expected: step2. delete succeed. + */ + g_kvStoreNbDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step3. update (KEY_1, VALUE_1) to local db. + * @tc.expected: step3. update succeed. + */ + Value valueResult; + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. update (KEY_1, VALUE_2) to sync db. + * @tc.expected: step4. update succeed. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. get data from local db where key = KEY_1. + * @tc.expected: step5. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step6. get data from sync db where key = KEY_1. + * @tc.expected: step6. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step7. get data from local db where key = KEY_1. + * @tc.expected: step7. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step8. get data from sync db where key = KEY_1. + * @tc.expected: step8. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Put(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step9. get data from local db where key = KEY_1. + * @tc.expected: step9. return value is VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step10. get data from sync db where key = KEY_1. + * @tc.expected: step10. return value is VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(KEY_1, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_1); +} + +/* + * @tc.name: Pressure 001 + * @tc.desc: verify that the get interface can check the key params' effectiveness. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure001, TestSize.Level1) +{ + DistributedDB::Key eKey1, eKey2, eKey3, eKey4; + eKey1 = {}; + eKey2.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + eKey3.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); + eKey4 = { 'a', 'b', 'c', 'D', 'E', 'F', '2', '4', '5', 199, 1, 255, 0 }; + + /** + * @tc.steps: step1. get data from local db where key = eKey1. + * @tc.expected: step1. get failed and return INVALID_ARGS. + */ + Value valueResult; + DBStatus status = g_kvStoreNbDelegate->GetLocal(eKey1, valueResult); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step2. get data from local db where key = eKey2. + * @tc.expected: step2. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(eKey2, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step3. get data from local db where key = eKey3. + * @tc.expected: step3. get failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->GetLocal(eKey3, valueResult); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step4. get data from local db where key = eKey4. + * @tc.expected: step4. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(eKey4, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step5. get data from local db where key = eKey1. + * @tc.expected: step5. get failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Get(eKey1, valueResult); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step6. get data from local db where key = eKey2. + * @tc.expected: step6. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(eKey2, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step7. get data from local db where key = eKey. + * @tc.expected: step7. get failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Get(eKey3, valueResult); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step8. get data from local db where key = eKey4. + * @tc.expected: step8. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(eKey4, valueResult); + EXPECT_EQ(status, NOT_FOUND); +} +/* + * @tc.name: Pressure 002 + * @tc.desc: verify that the put interface can check key params 'effectiveness and 4M value. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure002, TestSize.Level1) +{ + DistributedDB::Value longValue; + longValue.assign(FOUR_M_LONG_STRING, (uint8_t)'a'); + + DistributedDB::Key eKey1, eKey2, eKey3, eKey4; + eKey1 = {}; + eKey2.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + eKey3.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); + eKey4 = { 'a', 'b', 'c', 'D', 'E', 'F', '2', '4', '5', 199, 1, 255, 0 }; + /** + * @tc.steps: step1. put (eKey1, longValue) to local db, among which ekey1 = {}. + * @tc.expected: step1. put failed, and return INVALID_ARGS. + */ + DBStatus status = g_kvStoreNbDelegate->PutLocal(eKey1, longValue); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step2. put (eKey2, longValue) to local db, among which size of ekey2 is 1K. + * @tc.expected: step2. put succeed. + */ + status = g_kvStoreNbDelegate->PutLocal(eKey2, longValue); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. put (eKey3, longValue) to local db, among which size of ekey3 is 1k + 1. + * @tc.expected: step3. put failed, and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->PutLocal(eKey3, longValue); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step4. put (eKey4, longValue) to local db, among which ekey4 contains [a-zA-Z0-9], [\0-\255], + * chinese and latins, and size of ekey4 is big than 0 and less or equal than 1K. + * @tc.expected: step4. put succeed. + */ + status = g_kvStoreNbDelegate->PutLocal(eKey4, longValue); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. put (eKey1, longValue) to sync db, among which ekey1 = {}. + * @tc.expected: step5. put failed, and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Put(eKey1, longValue); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step6. put (eKey2, longValue) to sync db, among which size of ekey2 is 1K. + * @tc.expected: step6. put succeed. + */ + status = g_kvStoreNbDelegate->Put(eKey2, longValue); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step7. put (eKey3, longValue) to sync db, among which size of ekey3 is 1k + 1. + * @tc.expected: step7. put failed, and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Put(eKey3, longValue); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step8. put (eKey4, longValue) to local db, among which ekey4 contains [a-zA-Z0-9], [\0-\255], + * chinese and latins, and size of ekey4 is big than 0 and less or equal than 1K. + * @tc.expected: step8. put succeed. + */ + status = g_kvStoreNbDelegate->Put(eKey4, longValue); + EXPECT_TRUE(status == OK); + g_kvStoreNbDelegate->DeleteLocal(eKey1); + g_kvStoreNbDelegate->DeleteLocal(eKey2); + g_kvStoreNbDelegate->DeleteLocal(eKey3); + g_kvStoreNbDelegate->DeleteLocal(eKey4); + g_kvStoreNbDelegate->Delete(eKey1); + g_kvStoreNbDelegate->Delete(eKey2); + g_kvStoreNbDelegate->Delete(eKey3); + g_kvStoreNbDelegate->Delete(eKey4); +} +/* + * @tc.name: Pressure 003 + * @tc.desc: verify that the update interface can check key params' effectiveness and value params' effectiveness. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure003, TestSize.Level1) +{ + DistributedDB::Value alphanumValue1, alphanumValue2, alphanumValue3, alphanumValue4; + alphanumValue1 = {}; + alphanumValue2.assign(FOUR_M_LONG_STRING, (uint8_t)'a'); + alphanumValue3.assign(FOUR_M_LONG_STRING + 1, (uint8_t)'b'); + int chr; + for (chr = 'a'; chr <= 'z'; ++chr) { + alphanumValue4.push_back(chr); + } + for (chr = 'A'; chr <= 'Z'; ++chr) { + alphanumValue4.push_back(chr); + } + for (chr = '0'; chr <= '9'; ++chr) { + alphanumValue4.push_back(chr); + } + alphanumValue4.push_back(1); + alphanumValue4.push_back(0); + alphanumValue4.push_back(255); + alphanumValue4.push_back(9); + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_2, VALUE_2)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_3, VALUE_3)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_4, VALUE_4)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_1, VALUE_1)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_2, VALUE_2)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_3, VALUE_3)) == OK); + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_4, VALUE_4)) == OK); + /** + * @tc.steps: step1. put (KEY_1, alphanumValue1) to local db, among which size of alphanumValue1 0. + * @tc.expected: step1. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_1, alphanumValue1)) == OK); + /** + * @tc.steps: step2. put (KEY_2, alphanumValue2) to local db, among which size of alphanumValue2 4M. + * @tc.expected: step2. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_2, alphanumValue2)) == OK); + /** + * @tc.steps: step3. put (KEY_3, alphanumValue3) to local db, among which size of alphanumValue3 (4M + 1). + * @tc.expected: step3. put failed and return INVALID_ARGS. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_3, alphanumValue3)) == INVALID_ARGS); + /** + * @tc.steps: step4. put (KEY_4, alphanumValue4) to local db, among which alphanumValue4 + * contains [a-zA-Z0-9], [\0-\255], chinese, latins. + * @tc.expected: step4. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->PutLocal(KEY_4, alphanumValue4)) == OK); + /** + * @tc.steps: step5. put (KEY_1, alphanumValue1) to sync db, among which size of alphanumValue1 0. + * @tc.expected: step5. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_1, alphanumValue1)) == OK); + /** + * @tc.steps: step6. put (KEY_2, alphanumValue2) to sync db, among which size of alphanumValue2 4M. + * @tc.expected: step6. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_2, alphanumValue2)) == OK); + /** + * @tc.steps: step7. put (KEY_3, alphanumValue3) to local db, among which size of alphanumValue3 (4M + 1). + * @tc.expected: step7. put failed and return INVALID_ARGS. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_3, alphanumValue3)) == INVALID_ARGS); + /** + * @tc.steps: step8. put (KEY_4, alphanumValue4) to local db, among which alphanumValue4 + * contains [a-zA-Z0-9], [\0-\255], chinese, latins. + * @tc.expected: step8. put succeed and return OK. + */ + EXPECT_TRUE((g_kvStoreNbDelegate->Put(KEY_4, alphanumValue4)) == OK); + g_kvStoreNbDelegate->DeleteLocal(KEY_1); + g_kvStoreNbDelegate->DeleteLocal(KEY_2); + g_kvStoreNbDelegate->DeleteLocal(KEY_3); + g_kvStoreNbDelegate->DeleteLocal(KEY_4); + g_kvStoreNbDelegate->Delete(KEY_1); + g_kvStoreNbDelegate->Delete(KEY_2); + g_kvStoreNbDelegate->Delete(KEY_3); + g_kvStoreNbDelegate->Delete(KEY_4); +} + +/* + * @tc.name: Pressure 004 + * @tc.desc: verify that check value params' effectiveness of delete interface. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure004, TestSize.Level1) +{ + DistributedDB::Key eKey1, eKey2, eKey3, eKey4; + eKey1 = {}; + eKey2.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + eKey3.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); + eKey4 = { 'a', 'b', 'c', 'D', 'E', 'F', '2', '4', '5', 199, 255, 0, 1 }; + DBStatus status = g_kvStoreNbDelegate->PutLocal(eKey1, VALUE_1); + EXPECT_TRUE(status == INVALID_ARGS); + status = g_kvStoreNbDelegate->PutLocal(eKey2, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->PutLocal(eKey3, VALUE_1); + EXPECT_TRUE(status == INVALID_ARGS); + status = g_kvStoreNbDelegate->PutLocal(eKey4, VALUE_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(eKey1, VALUE_2); + EXPECT_TRUE(status == INVALID_ARGS); + status = g_kvStoreNbDelegate->Put(eKey2, VALUE_2); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(eKey3, VALUE_2); + EXPECT_TRUE(status == INVALID_ARGS); + status = g_kvStoreNbDelegate->Put(eKey4, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. delete eKey1 from local db, where key = eKey1, which eKey1 is null. + * @tc.expected: step1. delete failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->DeleteLocal(eKey1); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step2. delete eKey2 from local db, where key = eKey2, which the size of eKey2 is 1K. + * @tc.expected: step2. delete succeed and return OK. + */ + status = g_kvStoreNbDelegate->DeleteLocal(eKey2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. delete eKey3 from local db, where key = eKey3, which the size of eKey3 is 1k + 1. + * @tc.expected: step3. delete failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->DeleteLocal(eKey3); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step4. delete eKey4 from local db, where key = eKey4, which eKey2 + * contains [a-zA-Z0-9], [\0-\255], chinese, latins. + * @tc.expected: step4. delete succeed and return OK. + */ + status = g_kvStoreNbDelegate->DeleteLocal(eKey4); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. delete eKey1 from sync db, where key = eKey1, which eKey1 is null. + * @tc.expected: step5. delete failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Delete(eKey1); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step6. delete eKey2 from sync db, where key = eKey2, which the size of eKey2 is 1K. + * @tc.expected: step6. delete succeed and return OK. + */ + status = g_kvStoreNbDelegate->Delete(eKey2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step7. delete eKey3 from sync db, where key = eKey3, which the size of eKey3 is 1k + 1. + * @tc.expected: step7. delete failed and return INVALID_ARGS. + */ + status = g_kvStoreNbDelegate->Delete(eKey3); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step8. delete eKey4 from sync db, where key = eKey4, which eKey2 + * contains [a-zA-Z0-9], [\0-\255], chinese, latins. + * @tc.expected: step8. delete succeed and return OK. + */ + status = g_kvStoreNbDelegate->Delete(eKey4); + EXPECT_TRUE(status == OK); +} +/* + * @tc.name: Pressure 005 + * @tc.desc: verify that can operate a super huge key and a super huge value. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure005, TestSize.Level2) +{ + DistributedDB::Key longKey; + DistributedDB::Value longValue1, longValue2; + longKey.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + longValue1.assign(FOUR_M_LONG_STRING, (uint8_t)'b'); + longValue2.assign(FOUR_M_LONG_STRING, (uint8_t)'c'); + /** + * @tc.steps: step1. put a longkey and longvalue (longKey, longValue1) to local db. + * @tc.expected: step1. put succeed and return OK. + */ + DBStatus status = g_kvStoreNbDelegate->PutLocal(longKey, longValue1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. put a longkey and longvalue (longKey, longValue2) to sync db. + * @tc.expected: step2. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->Put(longKey, longValue2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. get data from local db where key = longKey. + * @tc.expected: step3. succeed get value is longValue1. + */ + Value valueResult; + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, longValue1); + /** + * @tc.steps: step4. get data from sync db where key = longKey. + * @tc.expected: step4. succeed get value is longValue2. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, longValue2); + /** + * @tc.steps: step5. put a longkey and longvalue (longKey, longValue2) to local db. + * @tc.expected: step5. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->PutLocal(longKey, longValue2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step6. put a longkey and longvalue (longKey, longValue1) to sync db. + * @tc.expected: step6. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->Put(longKey, longValue1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step7. get data from local db where key = longKey. + * @tc.expected: step7. succeed get value is longValue2. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, longValue2); + /** + * @tc.steps: step8. get data from sync db where key = longKey. + * @tc.expected: step8. succeed get value is longValue1. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, longValue1); + /** + * @tc.steps: step9. delete data from local db where key = longKey. + * @tc.expected: step9. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->DeleteLocal(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step10. delete data from sync db where key = longKey. + * @tc.expected: step10. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->Delete(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step11. get data from local db where key = longKey. + * @tc.expected: step11. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_TRUE(status == NOT_FOUND); + /** + * @tc.steps: step12. get data from sync db where key = longKey. + * @tc.expected: step12. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_TRUE(status == NOT_FOUND); +} + +void PressureForBasicCrud(Key &longKey, Value &valueResult) +{ + /** + * @tc.steps: step1. put (longKey, VALUE_1) to local db. + * @tc.expected: step1. put succeed and return OK. + */ + DBStatus status = g_kvStoreNbDelegate->PutLocal(longKey, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. put (longKey, VALUE_2) to sync db. + * @tc.expected: step2. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->Put(longKey, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. get data from local db where key = longKey. + * @tc.expected: step3. get success and value = VALUE_1. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step4. get data from sync db where key = longKey. + * @tc.expected: step4. get success and value = VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step5. delete data from local db where key = longKey. + * @tc.expected: step5. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->DeleteLocal(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step6. delete data from sync db where key = longKey. + * @tc.expected: step6. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->Delete(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step7. get data from local db where key = longKey. + * @tc.expected: step7. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step8. get data from sync db where key = longKey. + * @tc.expected: step8. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step9. put (longKey, VALUE_1) to local db. + * @tc.expected: step9. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->PutLocal(longKey, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step10. put (longKey, VALUE_2) to sync db. + * @tc.expected: step10. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->Put(longKey, VALUE_2); + EXPECT_TRUE(status == OK); +} + +/* + * @tc.name: Pressure 006 + * @tc.desc: verify that can crud one key-value repeately. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure006, TestSize.Level1) +{ + DistributedDB::Key longKey; + longKey.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + Value valueResult; + PressureForBasicCrud(longKey, valueResult); + /** + * @tc.steps: step1. get data from local db where key = longKey. + * @tc.expected: step1. get success and value = VALUE_1. + */ + DBStatus status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step2. get data from sync db where key = longKey. + * @tc.expected: step2. get success and value = VALUE_2. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step3. put (longKey, VALUE_2) to local db. + * @tc.expected: step3. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->PutLocal(longKey, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. put (longKey, VALUE_1) to sync db. + * @tc.expected: step4. put succeed and return OK. + */ + status = g_kvStoreNbDelegate->Put(longKey, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. get data from local db where key = longKey. + * @tc.expected: step5. get success and value = VALUE_2. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_2); + /** + * @tc.steps: step6. get data from sync db where key = longKey. + * @tc.expected: step6. get success and value = VALUE_1. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, OK); + EXPECT_EQ(valueResult, VALUE_1); + /** + * @tc.steps: step7. delete data from local db where key = longKey. + * @tc.expected: step7. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->DeleteLocal(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step8. delete data from sync db where key = longKey. + * @tc.expected: step8. delete succeed and return ok. + */ + status = g_kvStoreNbDelegate->Delete(longKey); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step9. get data from local db where key = longKey. + * @tc.expected: step9. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->GetLocal(longKey, valueResult); + EXPECT_EQ(status, NOT_FOUND); + /** + * @tc.steps: step10. get data from sync db where key = longKey. + * @tc.expected: step10. get failed and return NOT_FOUND. + */ + status = g_kvStoreNbDelegate->Get(longKey, valueResult); + EXPECT_EQ(status, NOT_FOUND); +} + +vector GenerateDataForBatch(vector &KeyCrud) +{ + DistributedDB::Key key1, key2, key3, key4, key5, key6, key7, key8; + key1 = {'a', 'b', 'c'}; + key2 = {'a', 'b', 'c', 'd', 'e'}; + key3 = {'a', 'b', 'c'}; + key4 = {'a', 'b', 'c', 'd', 'a', 's', 'd'}; + key5 = {'a', 'b', 'c', 'd', 's'}; + key6 = {'b', 'c'}; + key7 = {'d', 'a', 'b'}; + key8 = {'d', 'a', 'b', 'c'}; + KeyCrud = { + key1, key2, key3, + key4, key5, key6, + key7, key8 + }; + DistributedDB::Value value1, value2, value3, value4, value5, value6, value7, value8; + value1 = {'l', 'o', 'c', 'a', 'l', 'V', '1'}; + value2 = {'l', 'o', 'c', 'a', 'l', 'V', '2'}; + value3 = {'s', 'y', 'n', 'V', '1'}; + value4 = {'s', 'y', 'n', 'V', '2'}; + value5 = {'s', 'y', 'n', 'V', '3'}; + value6 = {'s', 'y', 'n', 'V', '4'}; + value7 = {'s', 'y', 'n', 'V', '5'}; + value8 = {'s', 'y', 'n', 'V', '6'}; + vector ValueCrud = { + value1, value2, value3, + value4, value5, value6, + value7, value8 + }; + return ValueCrud; +} + +void PutData(vector &KeyCrud, vector &ValueCrud) +{ + ASSERT_TRUE(g_kvStoreNbDelegate != nullptr); + DBStatus status; + int index; + for (index = 0; index < LOCAL_OPER_CNT; ++index) { + status = g_kvStoreNbDelegate->PutLocal(KeyCrud[index], ValueCrud[index]); + EXPECT_TRUE(status == OK); + } + for (index = LOCAL_OPER_CNT; index < LOCAL_OPER_CNT + NATIVE_OPER_CNT; ++index) { + status = g_kvStoreNbDelegate->Put(KeyCrud[index], ValueCrud[index]); + EXPECT_TRUE(status == OK); + } +} + +/* + * @tc.name: Pressure 007 + * @tc.desc: verify that can get batch data use keyprefix from db. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure007, TestSize.Level0) +{ + vector KeyCrud; + vector ValueCrud = GenerateDataForBatch(KeyCrud); + PutData(KeyCrud, ValueCrud); + DistributedDB::Key key9, key10; + key9 = { 'a', 'b' }; + key10 = { 'a', 'b', 'c', 'd', 'e' }; + std::vector entries; + /** + * @tc.steps: step1. get entries from db use keyprefix key9 = "ab". + * @tc.expected: step1. get entries succeed and entries contains ({abcde}, {localV2}), + * "abc, syncV1"; "abcdasd, syncV2". + */ + DBStatus status = g_kvStoreNbDelegate->GetEntries(key9, entries); + EXPECT_TRUE(status == OK); + int iCount = 3; + DistributedDB::Key rKey[iCount]; + rKey[0] = KeyCrud[2]; + rKey[1] = KeyCrud[3]; + rKey[2] = KeyCrud[4]; + DistributedDB::Value rValue[iCount]; + rValue[0] = ValueCrud[2]; + rValue[1] = ValueCrud[3]; + rValue[2] = ValueCrud[4]; + + for (unsigned long index = 0; index < entries.size(); index++) { + EXPECT_EQ(entries[index].key, rKey[index]); + EXPECT_EQ(entries[index].value, rValue[index]); + } + /** + * @tc.steps: step2. get entries from db use keyprefix key10 = "abcde". + * @tc.expected: step2. get entries failed and return NOT_FOUND + */ + status = g_kvStoreNbDelegate->GetEntries(key10, entries); + EXPECT_TRUE(status == NOT_FOUND); + g_kvStoreNbDelegate->DeleteLocal(KeyCrud[0]); + g_kvStoreNbDelegate->DeleteLocal(KeyCrud[1]); + g_kvStoreNbDelegate->Delete(KeyCrud[2]); + g_kvStoreNbDelegate->Delete(KeyCrud[3]); + g_kvStoreNbDelegate->Delete(KeyCrud[4]); + g_kvStoreNbDelegate->Delete(KeyCrud[5]); + g_kvStoreNbDelegate->Delete(KeyCrud[6]); + g_kvStoreNbDelegate->Delete(KeyCrud[7]); +} + +/* + * @tc.name: Pressure 008 + * @tc.desc: verify that key effectiveness of getEntries interface. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure008, TestSize.Level1) +{ + DistributedDB::Key longKey1, longKey2, longKey3; + longKey1.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + longKey2.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); + longKey3 = { 'a', 'b', 'c', 'D', 'E', 'F', '2', '4', '5', 199, 255, 0 }; + Value value1, value3; + DBStatus status = g_kvStoreNbDelegate->PutLocal(longKey1, value1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Put(longKey3, value3); + EXPECT_TRUE(status == OK); + DistributedDB::Key key9, key10; + key9 = { 'a', 'b' }; + key10 = { 'a', 'b', 'c', 'd', 'e' }; + std::vector entries; + /** + * @tc.steps: step1. get entries from db use keyPrefix = longKey1 size of which is 1K. + * @tc.expected: step1. get entries failed and return NOT_FOUND + */ + status = g_kvStoreNbDelegate->GetEntries(longKey1, entries); + EXPECT_TRUE(status == NOT_FOUND); + /** + * @tc.steps: step2. get entries from db use keyPrefix = longKey2 size of which is 1k + 1. + * @tc.expected: step2. get entries failed and return INVALID_ARGS + */ + status = g_kvStoreNbDelegate->GetEntries(longKey2, entries); + EXPECT_TRUE(status == INVALID_ARGS); + /** + * @tc.steps: step3. get entries from db use keyPrefix = longKey3 + * which contains [a-zA-Z0-9], [\0-\255], chinese, latins. + * @tc.expected: step3. get entries success and return the entries has longKey3 keyPrefix. + */ + status = g_kvStoreNbDelegate->GetEntries(longKey3, entries); + EXPECT_TRUE(status == OK); + EXPECT_TRUE(entries.size() == 1); + g_kvStoreNbDelegate->DeleteLocal(longKey1); + g_kvStoreNbDelegate->Delete(longKey3); +} + +/* + * @tc.name: Pressure 010 + * @tc.desc: verify can't crud after db is closed. + * @tc.type: Fault injection + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure010, TestSize.Level2) +{ +#ifdef RUNNING_ON_SIMULATED_ENV + KvStoreNbDelegate *kvStoreNbDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + kvStoreNbDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && kvStoreNbDelegate != nullptr); + ASSERT_EQ(manager->CloseKvStore(kvStoreNbDelegate), OK); + /** + * @tc.steps: step1. put (KEY_1, VALUE_1) to local db. + * @tc.expected: step1. put failed and return ERROR. + */ + Value valueResult; + DBStatus status = kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step2. put (KEY_1, VALUE_1) to sync db. + * @tc.expected: step2. put failed and return ERROR. + */ + status = kvStoreNbDelegate->Put(KEY_2, VALUE_2); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step3. delete data from local db where key = KEY_1. + * @tc.expected: step3. delete failed and return ERROR. + */ + status = kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step4. delete data from sync db where key = KEY_2. + * @tc.expected: step4. delete failed and return ERROR. + */ + status = kvStoreNbDelegate->Delete(KEY_2); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step5. update (KEY_1, VALUE_2) to local db. + * @tc.expected: step5. update failed and return ERROR. + */ + status = kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step6. update (KEY_2, VALUE_1) to sync db. + * @tc.expected: step6. update failed and return ERROR. + */ + status = kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == DB_ERROR); + /** + * @tc.steps: step7. get data from local db where key = KEY_1. + * @tc.expected: step7. get failed and return ERROR. + */ + status = kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, DB_ERROR); + /** + * @tc.steps: step8. get data from sync db where key = KEY_2. + * @tc.expected: step8. get failed and return ERROR. + */ + status = kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, DB_ERROR); + DistributedDB::Key key9; + key9 = { 'a', 'b' }; + std::vector entries; + /** + * @tc.steps: step9. get batch data from db with keyPrefix = 'ab'. + * @tc.expected: step9. get failed and return ERROR. + */ + status = kvStoreNbDelegate->GetEntries(key9, entries); + EXPECT_EQ(status, DB_ERROR); + + kvStoreNbDelegate->DeleteLocal(KEY_1); + kvStoreNbDelegate->Delete(KEY_2); + if (!g_option.isMemoryDb) { + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + } + kvStoreNbDelegate = nullptr; + delete manager; + manager = nullptr; +#endif +} + +/* + * @tc.name: Pressure 011 + * @tc.desc: verify can't crud after db is deleted. + * @tc.type: Fault injection + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Pressure011, TestSize.Level3) +{ + std::this_thread::sleep_for(std::chrono::seconds(UNIQUE_SECOND * 10)); + /** + * @tc.steps: step1. put (KEY_1, VALUE_1) to local db. + * @tc.expected: step1. put failed and return ERROR. + */ + Value valueResult; + DBStatus status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. put (KEY_1, VALUE_1) to sync db. + * @tc.expected: step2. put failed and return ERROR. + */ + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step3. delete data from local db where key = KEY_1. + * @tc.expected: step3. delete failed and return ERROR. + */ + status = g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step4. delete data from sync db where key = KEY_2. + * @tc.expected: step4. delete failed and return ERROR. + */ + status = g_kvStoreNbDelegate->Delete(KEY_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step5. update (KEY_1, VALUE_2) to local db. + * @tc.expected: step5. update failed and return ERROR. + */ + status = g_kvStoreNbDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step6. update (KEY_2, VALUE_1) to sync db. + * @tc.expected: step6. update failed and return ERROR. + */ + status = g_kvStoreNbDelegate->Put(KEY_2, VALUE_1); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step7. get data from local db where key = KEY_1. + * @tc.expected: step7. get failed and return ERROR. + */ + status = g_kvStoreNbDelegate->GetLocal(KEY_1, valueResult); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step8. get data from sync db where key = KEY_2. + * @tc.expected: step8. get failed and return ERROR. + */ + status = g_kvStoreNbDelegate->Get(KEY_2, valueResult); + EXPECT_EQ(status, OK); + DistributedDB::Key key9 = { 'a', 'b' }; + /** + * @tc.steps: step9. get batch data from db with keyPrefix = 'ab'. + * @tc.expected: step9. get failed and return ERROR. + */ + std::vector entries; + status = g_kvStoreNbDelegate->GetEntries(key9, entries); + EXPECT_EQ(status, NOT_FOUND); + status = g_kvStoreNbDelegate->DeleteLocal(KEY_1); + EXPECT_TRUE(status == OK); + status = g_kvStoreNbDelegate->Delete(KEY_2); + EXPECT_TRUE(status == OK); +} + +void StartThreadForLongReadRead() +{ + std::random_device randDevReadKeyNo; + std::mt19937 genRandReadKeyNo(randDevReadKeyNo()); + std::uniform_int_distribution disRandReadKeyNo(READ_RECORDS_NUM_START, READ_RECORDS_NUM_END); + + unsigned long randKeyNo; + unsigned int thrCurId = 0; + + SysTime start; + SysDurTime dur; + double operInterval = 0.0; + start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + + /** + * @tc.steps: step1. start 6 thread read the db randly at the same time for a long time. + * @tc.expected: step1. can read and read rightly and has no mistakes. + */ + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + auto threadParam = new (std::nothrow) ConcurParam; + ASSERT_NE(threadParam, nullptr); + threadParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(threadParam->entryPtr_, nullptr); + threadParam->threadId_ = thrCurId++; + threadParam->tag_ = READ; + randKeyNo = disRandReadKeyNo(genRandReadKeyNo); + Entry entryCurrent; + GenerateRecord(randKeyNo, entryCurrent); + operInterval = NbCalculateTime(threadParam, entryCurrent, start, dur); + } +} + +/* + * @tc.name: Concurrency 002 + * @tc.desc: verify can read and read Concurrency for a long time and has no mistakes. + * @tc.type: LongTime Test + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Concurrency002, TestSize.Level3) +{ + KvStoreNbDelegate *kvStoreNbDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + kvStoreNbDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && kvStoreNbDelegate != nullptr); + + vector entriesBatch; + vector allKeys; + GenerateRecords(RECORDS_NUM_THOUSAND, DEFAULT_START, allKeys, entriesBatch); + + DBStatus status; + for (unsigned int index = 0; index < RECORDS_NUM_THOUSAND; index++) + { + status = kvStoreNbDelegate->Put(entriesBatch[index].key, entriesBatch[index].value); + EXPECT_EQ(status, OK); + } + + vector valueResult; + kvStoreNbDelegate->GetEntries(KEY_EMPTY, valueResult); + EXPECT_EQ(valueResult.size(), RECORDS_NUM_THOUSAND); + + StartThreadForLongReadRead(); + + while (entriesBatch.size() > 0) { + status = DistributedDBNbTestTools::Delete(*kvStoreNbDelegate, entriesBatch[0].key); + EXPECT_EQ(status, OK); + entriesBatch.erase(entriesBatch.begin()); + } + MST_LOG("entriesBatch.size() = %zu", entriesBatch.size()); + EXPECT_TRUE(EndCaseDeleteDB(manager, kvStoreNbDelegate, STORE_ID_2, g_option.isMemoryDb)); +} + +void StartThreadForLongReadWrite(vector &entriesBatch) +{ + std::vector threads; + std::random_device randDevReadKeyNo, randDevWriteKeyNo, randDevTag; + std::mt19937 genRandReadKeyNo(randDevReadKeyNo()), genRandWriteKeyNo(randDevWriteKeyNo()), + genRandTag(randDevTag()); + std::uniform_int_distribution disRandReadKeyNo(READ_RECORDS_NUM_START, READ_RECORDS_NUM_END); + std::uniform_int_distribution disRandWriteKeyNo(WRITE_RECORDS_NUM_START, WRITE_RECORDS_NUM_END); + std::uniform_int_distribution disRandTag(READ, WRITE); + unsigned long randKeyNo; + unsigned int threadCurId = 0; + int randTag; + SysTime start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + /** + * @tc.steps: step1. start 6 thread read and write the db randly at the same time for a long time. + * @tc.expected: step1. can read and write rightly and has no mistakes. + */ + SysDurTime dur; + double operInterval = 0.0; + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + auto threadParam = new (std::nothrow) ConcurParam; + ASSERT_NE(threadParam, nullptr); + threadParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(threadParam->entryPtr_, nullptr); + threadParam->threadId_ = threadCurId++; + randTag = disRandTag(genRandTag); + threadParam->tag_ = static_cast(randTag); + if (randTag == READ) { + randKeyNo = disRandReadKeyNo(genRandReadKeyNo); + } else { + randKeyNo = disRandWriteKeyNo(genRandWriteKeyNo); + } + Entry entryCurrent; + GenerateRecord(randKeyNo, entryCurrent); + if ((randTag == WRITE) && (randKeyNo > READ_RECORDS_NUM_END)) { + entriesBatch.push_back(entryCurrent); + } + operInterval = NbCalculateTime(threadParam, entryCurrent, start, dur); + } +} + +/* + * @tc.name: Concurrency 004 + * @tc.desc: verify can read and write Concurrency for a long time. + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Concurrency004, TestSize.Level3) +{ + KvStoreNbDelegate *kvStoreNbDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + kvStoreNbDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && kvStoreNbDelegate != nullptr); + + vector entriesBatch; + vector allKeys; + GenerateRecords(RECORDS_NUM_THOUSAND, DEFAULT_START, allKeys, entriesBatch); + + DBStatus status = DistributedDBNbTestTools::PutBatch(*kvStoreNbDelegate, entriesBatch); + EXPECT_EQ(status, DBStatus::OK); + vector valueResult; + status = DistributedDBNbTestTools::GetEntries(*kvStoreNbDelegate, KEY_SEARCH_4, valueResult); + EXPECT_EQ(status, DBStatus::OK); + MST_LOG("value size %zu", valueResult.size()); + + StartThreadForLongReadWrite(entriesBatch); + + for (unsigned int index = 0; index < RECORDS_NUM_THOUSAND; index++) { + status = DistributedDBNbTestTools::Delete(*kvStoreNbDelegate, entriesBatch[index].key); + EXPECT_EQ(status, OK); + } + MST_LOG("entriesBatch.size() = %zu", entriesBatch.size()); + EXPECT_TRUE(EndCaseDeleteDB(manager, kvStoreNbDelegate, STORE_ID_2, g_option.isMemoryDb)); +} + +void JudgeInLongWriteWrite(vector &entriesBatch, ConcurParam *threadParam, Entry &entryCurrent) +{ + bool isExist = false; + if (threadParam->tag_ == WRITE) { + for (auto &entry : entriesBatch) { + if (CompareVector(entry.key, entryCurrent.key)) { + isExist = true; + } + } + if (!isExist) { + entriesBatch.push_back(entryCurrent); + } + } else { + for (unsigned long index = 0; index < entriesBatch.size(); ++index) { + if (CompareVector(entriesBatch[index].key, entryCurrent.key)) { + entriesBatch.erase(entriesBatch.begin() + index); + } + } + } +} + +void StartThreadForLongWriteWrite(vector &entriesBatch) +{ + std::vector threads; + std::random_device randDevWriteKeyNo, randDevDeleteKeyNo, randDevTag; + std::mt19937 genRandWriteKeyNo(randDevWriteKeyNo()), genRandDeleteKeyNo(randDevDeleteKeyNo()), + genRandTag(randDevTag()); + std::uniform_int_distribution disRandWriteKeyNo(WRITE_RECORDS_NUM_START, WRITE_RECORDS_NUM_END); + std::uniform_int_distribution disRandDeleteKeyNo(DELETE_RECORDS_NUM_START, DELETE_RECORDS_NUM_END); + std::uniform_int_distribution disRandTag(WRITE, DELETE); + unsigned long randKeyNo, randRandTag; + unsigned int threadCurId = 0; + + SysTime start; + SysDurTime dur; + double operInterval = 0.0; + + /** + * @tc.steps: step1. start 6 thread write the db randly at the same time for a long time. + * @tc.expected: step1. can write rightly and has no mistake. + */ + start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + auto thParam = new (std::nothrow) ConcurParam; + ASSERT_NE(thParam, nullptr); + thParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(thParam->entryPtr_, nullptr); + thParam->threadId_ = threadCurId++; + randRandTag = disRandTag(genRandTag); + thParam->tag_ = static_cast(randRandTag); + if (randRandTag == WRITE) { + randKeyNo = disRandWriteKeyNo(genRandWriteKeyNo); + } else { + randKeyNo = disRandDeleteKeyNo(genRandDeleteKeyNo); + } + Entry entryCurrent; + GenerateRecord(randKeyNo, entryCurrent); + JudgeInLongWriteWrite(entriesBatch, thParam, entryCurrent); + operInterval = NbCalculateTime(thParam, entryCurrent, start, dur); + } +} + +/* + * @tc.name: Concurrency 006 + * @tc.desc: verify can write and write Concurrency for a long time. + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbCrudTest, Concurrency006, TestSize.Level3) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + vector entryBatches; + vector allKeys; + GenerateRecords(RECORDS_NUM_THOUSAND, DEFAULT_START, allKeys, entryBatches); + + DBStatus status = DistributedDBNbTestTools::PutBatch(*delegate, entryBatches); + EXPECT_EQ(status, DBStatus::OK); + vector valueResult; + status = DistributedDBNbTestTools::GetEntries(*delegate, KEY_SEARCH_4, valueResult); + EXPECT_EQ(status, DBStatus::OK); + MST_LOG("value size %zu", valueResult.size()); + + StartThreadForLongWriteWrite(entryBatches); + + for (unsigned int index = 0; index < entryBatches.size(); index++) { + status = DistributedDBNbTestTools::Delete(*delegate, entryBatches[index].key); + EXPECT_TRUE(status == OK || status == NOT_FOUND); + } + MST_LOG("entryBatches.size() = %zu", entryBatches.size()); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_option.isMemoryDb)); +} +} diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_test.cpp new file mode 100755 index 000000000..0f3fa823f --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_test.cpp @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_nb_cursor_testcase.h" +#include "distributeddb_nb_test_tools.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" + +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; +namespace DistributeddbNbCursor { +KvStoreNbDelegate *g_nbCursorDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; +class DistributeddbNbCursorTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbCursorTest::SetUpTestCase(void) +{ +} + +void DistributeddbNbCursorTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbCursorTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_nbCursorDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_manager != nullptr && g_nbCursorDelegate != nullptr); +} + +void DistributeddbNbCursorTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_nbCursorDelegate, STORE_ID_1, g_option.isMemoryDb)); + RemoveDir(NB_DIRECTOR); +} + +/* + * @tc.name: ResultSetDb 001 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb001, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb001(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 002 + * @tc.desc: test MoveToNext, MoveToPrevious interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb002, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb002(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 003 + * @tc.desc: test Move interface with little data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb003, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb003(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 004 + * @tc.desc: test MoveToPostions interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb004, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb004(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 005 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb005, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb005(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 006 + * @tc.desc: test MoveToNext, MoveToPrevious interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb006, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb006(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 007 + * @tc.desc: test Move interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb007, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb007(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 008 + * @tc.desc: test MoveToPosition interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb008, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb008(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 009 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb009, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb009(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 010 + * @tc.desc: test MoveToNext, MoveToPrevious interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb010, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb010(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 011 + * @tc.desc: test Move interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb011, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb011(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 012 + * @tc.desc: test MoveToPosition interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb012, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb012(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 013 + * @tc.desc: test MoveToNext and Move interface can be called one record by one record. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb013, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb013(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 014 + * @tc.desc: test GetEntries that was not exist in db and if IsFirst, IsLast, MoveToFirst, MoveToLast, IsBeforeFirst + * IsAfterLast, and MoveToPrevious, MoveToNext interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb014, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb014(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 015 + * @tc.desc: test GetEntries that was not exist in db and if Move interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb015, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb015(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 016 + * @tc.desc: test GetEntries that was not exist in db and if MoveToPosition interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb016, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb016(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 017 + * @tc.desc: call CloseResultSet with nullptr returns INVALID_ARGS . + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb017, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb017(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 018 + * @tc.desc: the count of remainder recordset don't change after close one. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb018, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb018(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 019 + * @tc.desc: append, delete, modify operation can change the count of recordset + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb019, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb019(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 020 + * @tc.desc: GetEntries returns BUSY when the count of recordsets is equal or more than 5. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb020, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb020(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 021 + * @tc.desc: CloseKvStore returns BUSY before close all resultSet(s). + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb021, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb021(g_nbCursorDelegate, g_manager, false); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ResultSetDb 022 + * @tc.desc: rekey returns BUSY when the size of recordset is larger than 4M + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb022, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb022(false); +} +#endif + +/* + * @tc.name: ResultSetDb 023 + * @tc.desc: rekey returns OK when the size of recordset is equal to 4M + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb023, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb023(false); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: ResultSetDb 024 + * @tc.desc: GetEntries returns BUSY when Rekeying + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb024, TestSize.Level3) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb024(false); +} +#endif + +/* + * @tc.name: ResultSetDb 025 + * @tc.desc: Verify that multiple threads can call GetEntries() to get resultSet at the same time. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb025, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb025(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 026 + * @tc.desc: Verify that multiple threads can call GetEntries() to get the same resultSet at the same time. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb026, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb026(g_nbCursorDelegate, false); +} + +/* + * @tc.name: ResultSetDb 027 + * @tc.desc: the interfaces of different delegates opened by the same db doesn't affect each other. + * @tc.type: FUNC + * @tc.require: SR000D08KS + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, ResultSetDb027, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_FULL_ENTRY. + DistributedNbCursorTestcase::ResultSetDb027(false); +} + +/* + * @tc.name: CacheRowIdTest 001 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest001, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb001(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 002 + * @tc.desc: test MoveToNext, MoveToPrevious interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest002, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb002(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 003 + * @tc.desc: test Move interface with little data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest003, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb003(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 004 + * @tc.desc: test MoveToPostions interfaces with little data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest004, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb004(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 005 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest005, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb005(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 006 + * @tc.desc: test MoveToNext, MoveToPrevious interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest006, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb006(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 007 + * @tc.desc: test Move interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest007, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb007(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 008 + * @tc.desc: test MoveToPosition interface with single 4M data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest008, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb008(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 009 + * @tc.desc: test GetCount, GetPosition, MoveToFirst, MoveToLast, IsFirst, IsLast, IsBeforeFirst and + * IsAfterLast interfaces with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest009, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb009(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 010 + * @tc.desc: test MoveToNext, MoveToPrevious interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest010, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb010(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 011 + * @tc.desc: test Move interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest011, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb011(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 012 + * @tc.desc: test MoveToPosition interface with 100 100k data. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest012, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb012(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 013 + * @tc.desc: test MoveToNext and Move interface can be called one record by one record. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest013, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb013(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 014 + * @tc.desc: test GetEntries that was not exist in db and if IsFirst, IsLast, MoveToFirst, MoveToLast, IsBeforeFirst + * IsAfterLast, and MoveToPrevious, MoveToNext interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest014, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb014(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 015 + * @tc.desc: test GetEntries that was not exist in db and if Move interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest015, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb015(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 016 + * @tc.desc: test GetEntries that was not exist in db and if MoveToPosition interface can return rightly. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest016, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb016(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 017 + * @tc.desc: call CloseResultSet with nullptr returns INVALID_ARGS . + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest017, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb017(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 018 + * @tc.desc: the count of remainder recordset don't change after close one. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest018, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb018(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 019 + * @tc.desc: append, delete, modify operation can change the count of recordset + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest019, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb019(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 020 + * @tc.desc: GetEntries returns BUSY when the count of recordsets is equal or more than 5. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest020, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb020(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 021 + * @tc.desc: CloseKvStore returns BUSY before close all resultSet(s). + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest021, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb021(g_nbCursorDelegate, g_manager, true); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: CacheRowIdTest 022 + * @tc.desc: rekey returns BUSY when the size of recordset is larger than 4M + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest022, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb022(true); +} +#endif + +/* + * @tc.name: CacheRowIdTest 023 + * @tc.desc: rekey returns OK when the size of recordset is equal to 4M + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest023, TestSize.Level1) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb023(true); +} + +#ifndef LOW_LEVEL_MEM_DEV +/* + * @tc.name: CacheRowIdTest 024 + * @tc.desc: GetEntries returns BUSY when Rekeying + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest024, TestSize.Level3) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb024(true); +} +#endif + +/* + * @tc.name: CacheRowIdTest 025 + * @tc.desc: Verify that multiple threads can call GetEntries() to get resultSet at the same time. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest025, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb025(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 026 + * @tc.desc: Verify that multiple threads can call GetEntries() to get the same resultSet at the same time. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest026, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb026(g_nbCursorDelegate, true); +} + +/* + * @tc.name: CacheRowIdTest 027 + * @tc.desc: the interfaces of different delegates opened by the same db doesn't affect each other. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbCursorTest, CacheRowIdTest027, TestSize.Level2) +{ + // test with RESULT_SET_CACHE_MODE = CACHE_ENTRY_ID_ONLY. + DistributedNbCursorTestcase::ResultSetDb027(true); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_testcase.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_testcase.cpp new file mode 100755 index 000000000..2919985b5 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_cursor_testcase.cpp @@ -0,0 +1,2029 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_nb_cursor_testcase.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "types.h" +#include "types_export.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" + +using namespace std; +using namespace std::chrono; +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace { +const unsigned int FIFTEEN_RECORDS = 15; +const unsigned int CURSOR_DIFFERENT_RECORDS = 102; +const unsigned long ONE_TENTH_M_LONG_STRING = 104858; +const unsigned int FOUR_BYTE_KEY = 4; +const unsigned int FIVE_BYTE_KEY = 5; +const unsigned int THREAD_NUM_START = 1; +const unsigned int THREAD_NUM_END = 4; +const unsigned int OPEN_DB_TIMES = 3; +const unsigned int DELEGATE_NUM = 3; +const unsigned int WAIT_FOR_OBSERVER_REKEY = 75000; +const int CURSOR_ORIGINAL_POSITION_NEGATIVE10 = -10; +const int CURSOR_ORIGINAL_POSITION_NEGATIVE1 = -1; +const int CURSOR_ORIGINAL_POSITION_0 = 0; +const int CURSOR_ORIGINAL_POSITION_1 = 1; +const int CURSOR_ORIGINAL_POSITION_2 = 2; +const int CURSOR_ORIGINAL_POSITION_40 = 40; +const int CURSOR_ORIGINAL_POSITION_50 = 50; +const int CURSOR_ORIGINAL_POSITION_60 = 60; +const int CURSOR_ORIGINAL_POSITION_99 = 99; +const int CURSOR_ORIGINAL_POSITION_100 = 100; +const int CURSOR_ORIGINAL_POSITION_120 = 120; + +const int CURSOR_POSITION_NEGATIVE5 = -5; +const int CURSOR_POSITION_NEGATIVE1 = -1; +const int CURSOR_POSITION_0 = 0; +const int CURSOR_POSITION_1 = 1; +const int CURSOR_POSITION_2 = 2; +const int CURSOR_POSITION_40 = 40; +const int CURSOR_POSITION_44 = 44; +const int CURSOR_POSITION_49 = 49; +const int CURSOR_POSITION_50 = 50; +const int CURSOR_POSITION_55 = 55; +const int CURSOR_POSITION_60 = 60; +const int CURSOR_POSITION_99 = 99; +const int CURSOR_POSITION_100 = 100; + +const int CURSOR_OFFSET_NEGATIVE65 = -65; +const int CURSOR_OFFSET_NEGATIVE60 = -60; +const int CURSOR_OFFSET_NEGATIVE50 = -50; +const int CURSOR_OFFSET_NEGATIVE45 = -45; +const int CURSOR_OFFSET_NEGATIVE2 = -2; +const int CURSOR_OFFSET_NEGATIVE1 = -1; +const int CURSOR_OFFSET_0 = 0; +const int CURSOR_OFFSET_1 = 1; +const int CURSOR_OFFSET_2 = 2; +const int CURSOR_OFFSET_5 = 5; +const int CURSOR_OFFSET_45 = 45; +const int CURSOR_OFFSET_50 = 50; +const int CURSOR_OFFSET_55 = 55; +const int CURSOR_OFFSET_60 = 60; +const int CURSOR_OFFSET_65 = 65; +DistributedDB::CipherPassword g_passwd1; + +struct PositionStatus01 { + DBStatus isGetSuccess = OK; + bool isFirst = true; + bool isLast = false; + bool isBeforeFirst = false; + bool isAfterLast = false; + int position = 0; +}; + +void SetResultSetCacheMode(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + if (isRowIdMode) { + int cacheMode = static_cast(ResultSetCacheMode::CACHE_ENTRY_ID_ONLY); + PragmaData data = static_cast(&cacheMode); + EXPECT_EQ(delegate->Pragma(RESULT_SET_CACHE_MODE, data), OK); + } else { + int cacheMode = static_cast(ResultSetCacheMode::CACHE_FULL_ENTRY); + PragmaData data = static_cast(&cacheMode); + EXPECT_EQ(delegate->Pragma(RESULT_SET_CACHE_MODE, data), OK); + } +} + +bool JudgePosition(KvStoreResultSet *&resultSet, const PositionStatus01 ¤tStatus) +{ + Entry entry; + bool bRes = true; + DBStatus status = resultSet->GetEntry(entry); + bRes = bRes && (status == currentStatus.isGetSuccess); + bool result = resultSet->IsFirst(); + bRes = bRes && (result == currentStatus.isFirst); + result = resultSet->IsLast(); + bRes = bRes && (result == currentStatus.isLast); + result = resultSet->IsBeforeFirst(); + bRes = bRes && (result == currentStatus.isBeforeFirst); + result = resultSet->IsAfterLast(); + bRes = bRes && (result == currentStatus.isAfterLast); + int position = resultSet->GetPosition(); + bRes = bRes && (position == currentStatus.position); + return bRes; +} +} + +void DistributedNbCursorTestcase::ResultSetDb001(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); + + /** + * @tc.steps: step3. call GetPosition and MoveToPrevious interface. + * @tc.expected: step3. when the Current position is -1, can't do MoveToPrevious, and if do, it can't effect at all. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_TRUE(resultSet->MoveToPrevious() == false); + + /** + * @tc.steps: step4. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast and GetPostion interface. + * @tc.expected: step4. when the Current position is -1, other position judge interface can return right result. + */ + PositionStatus01 currentStatus1 = {NOT_FOUND, false, false, true, false, CURSOR_POSITION_NEGATIVE1}; + EXPECT_TRUE(JudgePosition(resultSet, currentStatus1)); + + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. Move success. + */ + EXPECT_TRUE(resultSet->MoveToFirst() == true); + /** + * @tc.steps: step6. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step6. when the Current position is 0, other position judge interface can return right result. + */ + PositionStatus01 currentStatus2 = {OK, true, false, false, false, CURSOR_POSITION_0}; + EXPECT_TRUE(JudgePosition(resultSet, currentStatus2)); + /** + * @tc.steps: step7. call MoveToNext interface. + * @tc.expected: step7. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == true); + /** + * @tc.steps: step8. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step8. when the Current position is 1, other position judge interface can return right result. + */ + PositionStatus01 currentStatus3 = {OK, false, false, false, false, CURSOR_POSITION_1}; + EXPECT_TRUE(JudgePosition(resultSet, currentStatus3)); + /** + * @tc.steps: step9. call MoveToLast interface. + * @tc.expected: step9. Move success. + */ + EXPECT_TRUE(resultSet->MoveToLast() == true); + /** + * @tc.steps: step10. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step10. when the Current position is the last, + * other position judge interface can return right result. + */ + PositionStatus01 currentStatus4 = {OK, false, true, false, false, CURSOR_POSITION_99}; + EXPECT_TRUE(JudgePosition(resultSet, currentStatus4)); + /** + * @tc.steps: step11. call MoveToNext interface. + * @tc.expected: step11. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == false); + /** + * @tc.steps: step10. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step10. if call MoveToNext interface when the Current position is the last, + * other position judge interface can also return right result. + */ + PositionStatus01 currentStatus5 = {NOT_FOUND, false, false, false, true, CURSOR_POSITION_100}; + EXPECT_TRUE(JudgePosition(resultSet, currentStatus5)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb002(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + + sort(entries.begin(), entries.end(), DistributedTestTools::CompareKey); + + /** + * @tc.steps: step2. set the current position is -1, call MoveToNext, GetPostion and GetEntry interface looply. + * @tc.expected: step2. return values are all right. + */ + EXPECT_TRUE(DistributedDBNbTestTools::MoveToNextFromBegin(*resultSet, entries, CURSOR_POSITION_100)); + /** + * @tc.steps: step2. set the current position is 100, call MoveToPrevious, GetPostion, GetEntry looply. + * @tc.expected: step2. return values are all right. + */ + int positionGot = CURSOR_POSITION_NEGATIVE1; + Entry entry; + bool result = true; + for (int position = CURSOR_POSITION_100; position > CURSOR_POSITION_NEGATIVE1; --position) { + result = resultSet->MoveToPrevious(); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(result == true); + } else { + EXPECT_TRUE(result == false); + } + positionGot = resultSet->GetPosition(); + EXPECT_TRUE(positionGot == (position - CURSOR_POSITION_1)); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet->GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entries[position - 1].key)); + EXPECT_TRUE(CompareVector(entry.value, entries[position - 1].value)); + } else { + EXPECT_TRUE(resultSet->GetEntry(entry) == NOT_FOUND); + } + } + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} +namespace { +struct MoveStatus { + int offSet = 0; + bool moveResult = true; + int currentPosition = 0; + DBStatus status = OK; +}; + +bool MoveAndCheck(KvStoreResultSet *&resultSet, const MoveStatus status) +{ + bool result = true; + Entry entry; + bool mRes = resultSet->Move(status.offSet); + result = result && (mRes == status.moveResult); + int position = resultSet->GetPosition(); + result = result && (position == status.currentPosition); + DBStatus statusGot = resultSet->GetEntry(entry); + result = result && (statusGot == status.status); + return result; +} +} + +void DistributedNbCursorTestcase::ResultSetDb003(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + /** + * @tc.steps: step2. call Move interface move to offset 50 and check the result. + * @tc.expected: step2. move ok, and the position is 50, and can get entry. + */ + MoveStatus status1 = {CURSOR_OFFSET_50, true, CURSOR_POSITION_49, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step3. move ok, and the position is 50, and can get entry. + */ + MoveStatus status2 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_49, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call Move interface move to offset 60 by upstairs and check the result. + * @tc.expected: step4. Move failed, and the position is 100, and can't get entry. + */ + MoveStatus status3 = {CURSOR_OFFSET_60, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status3)); + /** + * @tc.steps: step5. call Move interface move to offset -50 by upstairs and check the result. + * @tc.expected: step5. move ok, and the position is 50, and can get entry. + */ + MoveStatus status4 = {CURSOR_OFFSET_NEGATIVE50, true, CURSOR_POSITION_50, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status4)); + /** + * @tc.steps: step6. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step6. move ok, and the position is 50, and can get entry. + */ + MoveStatus status5 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_50, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status5)); + /** + * @tc.steps: step7. call Move interface move to offset -60 by upstairs and check the result. + * @tc.expected: step7. Move failed, and the position is -1, and can't get entry. + */ + MoveStatus status6 = {CURSOR_OFFSET_NEGATIVE60, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status6)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} +namespace { +struct PositionStatus02 { + int originalPosition = 0; + bool moveResult = true; + int currentPosition = 0; + DBStatus status = OK; +}; + +bool MoveToPositionAndCheck(KvStoreResultSet *&resultSet, const PositionStatus02 status) +{ + bool result = true; + Entry entry; + bool mRes = resultSet->MoveToPosition(status.originalPosition); + result = result && (mRes == status.moveResult); + int position = resultSet->GetPosition(); + result = result && (position == status.currentPosition); + DBStatus statusGot = resultSet->GetEntry(entry); + result = result && (statusGot == status.status); + return result; +} +} +void DistributedNbCursorTestcase::ResultSetDb004(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + /** + * @tc.steps: step2. call MoveToPostion interface move to position 50 and check the result. + * @tc.expected: step2. MoveToPostion ok, and the position is 50, and can get entry. + */ + PositionStatus02 status1 = {CURSOR_ORIGINAL_POSITION_50, true, CURSOR_POSITION_50, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call MoveToPostion interface move to position 60 and check the result. + * @tc.expected: step3. MoveToPostion ok, and the position is 60, and can get entry. + */ + PositionStatus02 status2 = {CURSOR_ORIGINAL_POSITION_60, true, CURSOR_POSITION_60, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call MoveToPostion interface move to position -10 and check the result. + * @tc.expected: step4. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status3 = {CURSOR_ORIGINAL_POSITION_NEGATIVE10, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status3)); + /** + * @tc.steps: step5. call MoveToPostion interface move to position 120 and check the result. + * @tc.expected: step5. Move failed, and the position is 100, and can't get entry. + */ + PositionStatus02 status4 = {CURSOR_ORIGINAL_POSITION_120, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status4)); + /** + * @tc.steps: step6. call MoveToPostion interface move to position 100 and check the result. + * @tc.expected: step6. Move failed, and the position is 100, and can't get entry. + */ + PositionStatus02 status5 = {CURSOR_ORIGINAL_POSITION_100, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status5)); + /** + * @tc.steps: step7. call MoveToPostion interface move to position 0 and check the result. + * @tc.expected: step7. move OK, and the position is 0, and can get entry. + */ + PositionStatus02 status6 = {CURSOR_ORIGINAL_POSITION_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status6)); + /** + * @tc.steps: step8. call MoveToPostion interface move to position 99 and check the result. + * @tc.expected: step8. move OK, and the position is 99, and can get entry. + */ + PositionStatus02 status7 = {CURSOR_ORIGINAL_POSITION_99, true, CURSOR_POSITION_99, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status7)); + /** + * @tc.steps: step9. call MoveToPostion interface move to position 0 and check the result. + * @tc.expected: step9. move OK, and the position is 0, and can get entry. + */ + PositionStatus02 status8 = {CURSOR_ORIGINAL_POSITION_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status8)); + /** + * @tc.steps: step10. call MoveToPostion interface move to position -1 and check the result. + * @tc.expected: step10. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status9 = {CURSOR_ORIGINAL_POSITION_NEGATIVE1, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status9)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb005(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_RECORD); + EXPECT_TRUE(delegate->Put(entries[0].key, entries[0].value) == OK); + + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success and returned 1 records. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_RECORD); + /** + * @tc.steps: step3. call GetPosition, MoveToPrevious and GetPosition interface and check the result. + * @tc.expected: step3. GetPosition returned -1, MoveToPrevious returned false, and GetPosition still returned -1. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_TRUE(resultSet->MoveToPrevious() == false); + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + /** + * @tc.steps: step4. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast and GetPostion interface. + * @tc.expected: step4. when the Current position is -1, other position judge interface can return right result. + */ + PositionStatus01 positionStatus = {NOT_FOUND, false, false, true, false, CURSOR_POSITION_NEGATIVE1}; + EXPECT_TRUE(JudgePosition(resultSet, positionStatus)); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. Move success. + */ + EXPECT_TRUE(resultSet->MoveToFirst() == true); + /** + * @tc.steps: step6. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step6. when the Current position is 0, other position judge interface can return right result. + */ + positionStatus = {OK, true, true, false, false, CURSOR_POSITION_0}; + EXPECT_TRUE(JudgePosition(resultSet, positionStatus)); + /** + * @tc.steps: step7. call MoveToNext interface. + * @tc.expected: step7. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == false); + /** + * @tc.steps: step8. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step8. when the Current position is 1, other position judge interface can return right result. + */ + positionStatus = {NOT_FOUND, false, false, false, true, CURSOR_POSITION_1}; + EXPECT_TRUE(JudgePosition(resultSet, positionStatus)); + /** + * @tc.steps: step9. call MoveToLast interface. + * @tc.expected: step9. Move success. + */ + EXPECT_TRUE(resultSet->MoveToLast() == true); + /** + * @tc.steps: step10. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step10. when the Current position is 1, other position judge interface can return right result. + */ + positionStatus = {OK, true, true, false, false, CURSOR_POSITION_0}; + EXPECT_TRUE(JudgePosition(resultSet, positionStatus)); + /** + * @tc.steps: step11. call MoveToNext interface. + * @tc.expected: step11. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == false); + /** + * @tc.steps: step12. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step12. when the Current position is 1, other position judge interface can return right result. + */ + positionStatus = {NOT_FOUND, false, false, false, true, CURSOR_POSITION_1}; + EXPECT_TRUE(JudgePosition(resultSet, positionStatus)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb006(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_RECORD); + EXPECT_TRUE(delegate->Put(entries[0].key, entries[0].value) == OK); + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + + /** + * @tc.steps: step2. set the current position is -1, call MoveToNext, GetPostion and GetEntry interface looply. + * @tc.expected: step2. return values are all right. + */ + int currentPosition = CURSOR_POSITION_NEGATIVE1; + Entry entry; + bool result = true; + for (int position = CURSOR_POSITION_NEGATIVE1; position < ONE_RECORD; ++position) { + result = resultSet->MoveToNext(); + if (position < (ONE_RECORD - CURSOR_POSITION_1)) { + EXPECT_TRUE(result == true); + } else { + EXPECT_TRUE(result == false); + } + currentPosition = resultSet->GetPosition(); + EXPECT_TRUE(currentPosition == (position + CURSOR_POSITION_1)); + if (position < (ONE_RECORD - CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet->GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entries[position + CURSOR_POSITION_1].key)); + EXPECT_TRUE(CompareVector(entry.value, entries[position + CURSOR_POSITION_1].value)); + } else { + EXPECT_TRUE(resultSet->GetEntry(entry) == NOT_FOUND); + } + } + /** + * @tc.steps: step2. set the current position is 100, call MoveToPrevious, GetPostion, GetEntry looply. + * @tc.expected: step2. return values are all right. + */ + for (int position = ONE_RECORD; position > CURSOR_POSITION_NEGATIVE1; --position) { + result = resultSet->MoveToPrevious(); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(result == true); + } else { + EXPECT_TRUE(result == false); + } + currentPosition = resultSet->GetPosition(); + EXPECT_TRUE(currentPosition == (position - CURSOR_POSITION_1)); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet->GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entries[position - CURSOR_POSITION_1].key)); + EXPECT_TRUE(CompareVector(entry.value, entries[position - CURSOR_POSITION_1].value)); + } else { + EXPECT_TRUE(resultSet->GetEntry(entry) == NOT_FOUND); + } + } + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb007(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_RECORD); + EXPECT_TRUE(delegate->Put(entries[0].key, entries[0].value) == OK); + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + /** + * @tc.steps: step2. call Move interface move to offset 50 and check the result. + * @tc.expected: step2. move ok, and the position is 50, and can get entry. + */ + MoveStatus status1 = {CURSOR_OFFSET_1, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step3. move ok, and the position is 50, and can get entry. + */ + MoveStatus status2 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call Move interface move to offset 60 by upstairs and check the result. + * @tc.expected: step4. Move failed, and the position is 100, and can't get entry. + */ + MoveStatus status3 = {CURSOR_OFFSET_2, false, CURSOR_POSITION_1, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status3)); + /** + * @tc.steps: step5. call Move interface move to offset -50 by upstairs and check the result. + * @tc.expected: step5. move ok, and the position is 50, and can get entry. + */ + MoveStatus status4 = {CURSOR_OFFSET_NEGATIVE1, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status4)); + /** + * @tc.steps: step6. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step6. move ok, and the position is 50, and can get entry. + */ + MoveStatus status5 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status5)); + /** + * @tc.steps: step7. call Move interface move to offset -60 by upstairs and check the result. + * @tc.expected: step7. Move failed, and the position is -1, and can't get entry. + */ + MoveStatus status6 = {CURSOR_OFFSET_NEGATIVE2, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status6)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb008(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_RECORD); + EXPECT_TRUE(delegate->Put(entries[0].key, entries[0].value) == OK); + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_K, resultSet) == OK); + /** + * @tc.steps: step2. call MoveToPostion interface move to position 0 and check the result. + * @tc.expected: step2. MoveToPostion ok, and the position is 50, and can get entry. + */ + PositionStatus02 status1 = {CURSOR_ORIGINAL_POSITION_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call MoveToPostion interface move to position 1 and check the result. + * @tc.expected: step3. MoveToPostion false, and the position is 1, and can't get entry. + */ + PositionStatus02 status2 = {CURSOR_ORIGINAL_POSITION_1, false, CURSOR_POSITION_1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call MoveToPostion interface move to position -1 and check the result. + * @tc.expected: step4. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status3 = {CURSOR_ORIGINAL_POSITION_NEGATIVE1, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status3)); + /** + * @tc.steps: step5. call MoveToPostion interface move to position 2 and check the result. + * @tc.expected: step5. Move failed, and the position is 1, and can't get entry. + */ + PositionStatus02 status4 = {CURSOR_ORIGINAL_POSITION_2, false, CURSOR_POSITION_1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status4)); + /** + * @tc.steps: step6. call MoveToPostion interface move to position 1 and check the result. + * @tc.expected: step6. Move failed, and the position is 1, and can't get entry. + */ + PositionStatus02 status5 = {CURSOR_ORIGINAL_POSITION_1, false, CURSOR_POSITION_1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status5)); + /** + * @tc.steps: step7. call MoveToPostion interface move to position 0 and check the result. + * @tc.expected: step7. move OK, and the position is 0, and can get entry. + */ + PositionStatus02 status6 = {CURSOR_ORIGINAL_POSITION_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status6)); + /** + * @tc.steps: step8. call MoveToPostion interface move to position 1 and check the result. + * @tc.expected: step8. Move failed, and the position is 1, and can't get entry. + */ + PositionStatus02 status7 = {CURSOR_ORIGINAL_POSITION_1, false, CURSOR_POSITION_1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status7)); + /** + * @tc.steps: step9. call MoveToPostion interface move to position -1 and check the result. + * @tc.expected: step9. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status8 = {CURSOR_ORIGINAL_POSITION_NEGATIVE1, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status8)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb009(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSet) == OK); + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success and returned 10 records. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. call GetPosition, MoveToPrevious and GetPosition interface and check the result. + * @tc.expected: step3. GetPosition returned -1, MoveToPrevious returned false, and GetPosition still returned -1. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_TRUE(resultSet->MoveToPrevious() == false); + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + /** + * @tc.steps: step4. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast and GetPostion interface. + * @tc.expected: step4. when the Current position is -1, other position judge interface can return right result. + */ + PositionStatus01 position = {NOT_FOUND, false, false, true, false, CURSOR_POSITION_NEGATIVE1}; + EXPECT_TRUE(JudgePosition(resultSet, position)); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. Move success. + */ + EXPECT_TRUE(resultSet->MoveToFirst() == true); + /** + * @tc.steps: step6. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step6. when the Current position is 0, other position judge interface can return right result. + */ + position = {OK, true, false, false, false, CURSOR_POSITION_0}; + EXPECT_TRUE(JudgePosition(resultSet, position)); + /** + * @tc.steps: step7. call MoveToNext interface. + * @tc.expected: step7. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == true); + /** + * @tc.steps: step8. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step8. when the Current position is 1, other position judge interface can return right result. + */ + position = {OK, false, false, false, false, CURSOR_POSITION_1}; + EXPECT_TRUE(JudgePosition(resultSet, position)); + /** + * @tc.steps: step9. call MoveToLast interface. + * @tc.expected: step9. Move success. + */ + EXPECT_TRUE(resultSet->MoveToLast() == true); + /** + * @tc.steps: step10. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step10. when the Current position is 9, other position judge interface can return right result. + */ + position = {OK, false, true, false, false, CURSOR_POSITION_99}; + EXPECT_TRUE(JudgePosition(resultSet, position)); + /** + * @tc.steps: step11. call MoveToNext interface. + * @tc.expected: step11. Move success. + */ + EXPECT_TRUE(resultSet->MoveToNext() == false); + /** + * @tc.steps: step12. call GetEntry, IsFirst, IsLast, IsBeforeFirst, IsAfterLast, GetPostion interface. + * @tc.expected: step12. when the Current position is 10, other position judge interface can return right result. + */ + position = {NOT_FOUND, false, false, false, true, CURSOR_POSITION_100}; + EXPECT_TRUE(JudgePosition(resultSet, position)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb010(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSet) == OK); + sort(entries.begin(), entries.end(), DistributedTestTools::CompareKey); + /** + * @tc.steps: step2. set the current position is -1, call MoveToNext, GetPostion and GetEntry interface looply. + * @tc.expected: step2. return values are all right. + */ + EXPECT_TRUE(DistributedDBNbTestTools::MoveToNextFromBegin(*resultSet, entries, CURSOR_POSITION_100)); + /** + * @tc.steps: step3. set the current position is 100, call MoveToPrevious, GetPostion, GetEntry looply. + * @tc.expected: step3. return values are all right. + */ + int currentPosition = CURSOR_POSITION_NEGATIVE1; + Entry entry; + bool result = true; + for (int position = ONE_HUNDRED_RECORDS; position > CURSOR_POSITION_NEGATIVE1; --position) { + result = resultSet->MoveToPrevious(); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(result == true); + } else { + EXPECT_TRUE(result == false); + } + currentPosition = resultSet->GetPosition(); + EXPECT_TRUE(currentPosition == (position - CURSOR_POSITION_1)); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet->GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entries[position - CURSOR_POSITION_1].key)); + EXPECT_TRUE(CompareVector(entry.value, entries[position - CURSOR_POSITION_1].value)); + } else { + EXPECT_TRUE(resultSet->GetEntry(entry) == NOT_FOUND); + } + } + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb011(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSet) == OK); + /** + * @tc.steps: step2. call Move interface move to offset 45 and check the result. + * @tc.expected: step2. move ok, and the position is 44, and can get entry. + */ + MoveStatus status1 = {CURSOR_OFFSET_45, true, CURSOR_POSITION_44, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step3. move ok, and the position is 44, and can get entry. + */ + MoveStatus status2 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_44, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call Move interface move to offset 65 by upstairs and check the result. + * @tc.expected: step4. Move failed, and the position is 100, and can't get entry. + */ + MoveStatus status3 = {CURSOR_OFFSET_55, true, CURSOR_POSITION_99, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status3)); + MoveStatus status4 = {CURSOR_OFFSET_65, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status4)); + /** + * @tc.steps: step5. call Move interface move to offset -45 by upstairs and check the result. + * @tc.expected: step5. move ok, and the position is 55, and can get entry. + */ + MoveStatus status5 = {CURSOR_OFFSET_NEGATIVE45, true, CURSOR_POSITION_55, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status5)); + /** + * @tc.steps: step6. call Move interface move to offset 0 by upstairs and check the result. + * @tc.expected: step6. move ok, and the position is 50, and can get entry. + */ + MoveStatus status6 = {CURSOR_OFFSET_0, true, CURSOR_POSITION_55, OK}; + EXPECT_TRUE(MoveAndCheck(resultSet, status6)); + /** + * @tc.steps: step7. call Move interface move to offset -65 by upstairs and check the result. + * @tc.expected: step7. Move failed, and the position is -1, and can't get entry. + */ + MoveStatus status7 = {CURSOR_OFFSET_NEGATIVE65, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveAndCheck(resultSet, status7)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb012(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSet) == OK); + /** + * @tc.steps: step2. call MoveToPostion interface move to position 40 and check the result. + * @tc.expected: step2. Move OK, and the position is 40, and can get entry. + */ + PositionStatus02 status1 = {CURSOR_ORIGINAL_POSITION_40, true, CURSOR_POSITION_40, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status1)); + /** + * @tc.steps: step3. call MoveToPostion interface move to position 60 and check the result. + * @tc.expected: step3. Move failed, and the position is 60, and can't get entry. + */ + PositionStatus02 status2 = {CURSOR_ORIGINAL_POSITION_60, true, CURSOR_POSITION_60, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status2)); + /** + * @tc.steps: step4. call MoveToPostion interface move to position -1 and check the result. + * @tc.expected: step4. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status3 = {CURSOR_ORIGINAL_POSITION_NEGATIVE1, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status3)); + /** + * @tc.steps: step5. call MoveToPostion interface move to position 120 and check the result. + * @tc.expected: step5. Move failed, and the position is 100, and can't get entry. + */ + PositionStatus02 status4 = {CURSOR_ORIGINAL_POSITION_120, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status4)); + /** + * @tc.steps: step6. call MoveToPostion interface move to position 100 and check the result. + * @tc.expected: step6. Move failed, and the position is 100, and can't get entry. + */ + PositionStatus02 status5 = {CURSOR_ORIGINAL_POSITION_100, false, CURSOR_POSITION_100, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status5)); + /** + * @tc.steps: step7. call MoveToPostion interface move to position 0 and check the result. + * @tc.expected: step7. move OK, and the position is 0, and can get entry. + */ + PositionStatus02 status6 = {CURSOR_ORIGINAL_POSITION_0, true, CURSOR_POSITION_0, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status6)); + /** + * @tc.steps: step8. call MoveToPostion interface move to position 99 and check the result. + * @tc.expected: step8. move OK, and the position is 99, and can get entry. + */ + PositionStatus02 status7 = {CURSOR_ORIGINAL_POSITION_99, true, CURSOR_POSITION_99, OK}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status7)); + /** + * @tc.steps: step9. call MoveToPostion interface move to position -1 and check the result. + * @tc.expected: step9. Move failed, and the position is -1, and can't get entry. + */ + PositionStatus02 status8 = {CURSOR_ORIGINAL_POSITION_NEGATIVE1, false, CURSOR_POSITION_NEGATIVE1, NOT_FOUND}; + EXPECT_TRUE(MoveToPositionAndCheck(resultSet, status8)); + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb013(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + Entry entry4M, entry2M; + entry4M.key.assign(KEY_SIX_BYTE, 'k'); + entry4M.value.assign(FOUR_M_LONG_STRING, 'v'); + entry2M.key.assign(KEY_THIRTYTWO_BYTE, 'k'); + entry2M.value.assign(TWO_M_LONG_STRING, 'v'); + entries.push_back(entry4M); + entries.push_back(entry2M); + for (unsigned int index = 0; index < CURSOR_DIFFERENT_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSet) == OK); + /** + * @tc.steps: step2. call GetCount interface get number of records of cursor. + * @tc.expected: step2. the number is 102. + */ + EXPECT_TRUE(resultSet->GetCount() == CURSOR_DIFFERENT_RECORDS); + unsigned int position = 0; + while (position < CURSOR_DIFFERENT_RECORDS) { + /** + * @tc.steps: step3. call MoveToNext interface and check the result. + * @tc.expected: step3. move ok. + */ + if (position != (CURSOR_DIFFERENT_RECORDS - CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet->MoveToNext()); + position = resultSet->GetPosition(); + /** + * @tc.steps: step4. call Move interface by 1 offset and check the result. + * @tc.expected: step4. move ok. + */ + EXPECT_TRUE(resultSet->Move(CURSOR_OFFSET_1)); + /** + * @tc.steps: step5. call GetPosition interface by 1 offset and check the result. + * @tc.expected: step5. GetPosition ok and the position increased by 1 each loop. + */ + position = resultSet->GetPosition(); + EXPECT_TRUE(resultSet->MoveToPosition(++position)); + } else { + EXPECT_FALSE(resultSet->MoveToNext()); + /** + * @tc.steps: step4. call Move interface by 1 offset and check the result. + * @tc.expected: step4. move ok. + */ + EXPECT_FALSE(resultSet->Move(CURSOR_OFFSET_1)); + /** + * @tc.steps: step5. call GetPosition interface by 1 offset and check the result. + * @tc.expected: step5. GetPosition ok and the position increased by 1 each loop. + */ + position = resultSet->GetPosition(); + EXPECT_FALSE(resultSet->MoveToPosition(++position)); + } + } + + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb014(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet with the prefix = { 'a' }. + * @tc.expected: step1. get KvStoreResultSet success. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_A, resultSet), OK); + /** + * @tc.steps: step2. call GetCount interface get number of records of cursor. + * @tc.expected: step2. the number is 0. + */ + EXPECT_TRUE(resultSet->GetCount() == NO_RECORD); + /** + * @tc.steps: step3. call IsFirst interface check whether the current position is first. + * @tc.expected: step3. return false. + */ + EXPECT_TRUE(resultSet->IsFirst() == false); + /** + * @tc.steps: step4. call IsLast interface check whether the current position is last. + * @tc.expected: step4. return false. + */ + EXPECT_TRUE(resultSet->IsLast() == false); + /** + * @tc.steps: step5. call MoveToFirst interface check whether it can MoveToFirst. + * @tc.expected: step5. return false. + */ + EXPECT_TRUE(resultSet->MoveToFirst() == false); + /** + * @tc.steps: step6. call MoveToLast interface check whether it can MoveToLast. + * @tc.expected: step6. return false. + */ + EXPECT_TRUE(resultSet->MoveToLast() == false); + /** + * @tc.steps: step7. call IsBeforeFirst interface check whether it can MoveToLast. + * @tc.expected: step7. return false. + */ + EXPECT_TRUE(resultSet->IsBeforeFirst() == true); + /** + * @tc.steps: step8. call MoveToNext first and then call IsBeforeFirst interface check the result. + * @tc.expected: step8. MoveToNext can returns ok, but IsBeforeFirst still returns false. + */ + EXPECT_TRUE(resultSet->MoveToNext() == false); + EXPECT_TRUE(resultSet->IsBeforeFirst() == true); + /** + * @tc.steps: step9. call IsAfterLast interface check whether it can MoveToLast. + * @tc.expected: step9. return false. + */ + EXPECT_TRUE(resultSet->IsAfterLast() == true); + /** + * @tc.steps: step10. call MoveToPrevious first and then call IsAfterLast interface check the result. + * @tc.expected: step10. MoveToPrevious can returns ok, but IsAfterLast still returns false. + */ + EXPECT_TRUE(resultSet->MoveToPrevious() == false); + EXPECT_TRUE(resultSet->IsAfterLast() == true); + + /** + * @tc.steps: step11. close KvStoreResultSet. + * @tc.expected: step11. close success. + */ + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); +} + +void DistributedNbCursorTestcase::ResultSetDb015(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet with the prefix = { 'a' }. + * @tc.expected: step1. get KvStoreResultSet success. + */ + Entry entry; + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_A, resultSet), OK); + /** + * @tc.steps: step2. call GetCount interface get number of records of cursor. + * @tc.expected: step2. the number is 0. + */ + EXPECT_TRUE(resultSet->GetCount() == NO_RECORD); + /** + * @tc.steps: step3. call Move interface with the offset is 0. + * @tc.expected: step3. return false. + */ + EXPECT_TRUE(resultSet->Move(CURSOR_OFFSET_0) == false); + /** + * @tc.steps: step4. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step4. GetPosition returns -1, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_TRUE(resultSet->GetEntry(entry) == NOT_FOUND); + /** + * @tc.steps: step5. call Move interface with the offset is -1. + * @tc.expected: step5. return false. + */ + EXPECT_TRUE(resultSet->Move(CURSOR_OFFSET_NEGATIVE1) == false); + /** + * @tc.steps: step6. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step6. GetPosition returns -1, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_EQ(resultSet->GetEntry(entry), NOT_FOUND); + /** + * @tc.steps: step7. call Move interface with the offset is 5. + * @tc.expected: step7. return false. + */ + EXPECT_TRUE(resultSet->Move(CURSOR_OFFSET_5) == false); + /** + * @tc.steps: step8. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step8. GetPosition returns 0, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_0); + EXPECT_EQ(resultSet->GetEntry(entry), NOT_FOUND); + + /** + * @tc.steps: step11. close KvStoreResultSet. + * @tc.expected: step11. close success. + */ + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb016(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + std::vector entries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_HUNDRED_K_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, ONE_HUNDRED_RECORDS); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegate->Put(entries[index].key, entries[index].value) == OK); + } + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet with the prefix = { 'a' }. + * @tc.expected: step1. get KvStoreResultSet success. + */ + Entry entryGot; + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_A, resultSet), OK); + /** + * @tc.steps: step2. call GetCount interface get number of records of cursor. + * @tc.expected: step2. the number is 0. + */ + EXPECT_TRUE(resultSet->GetCount() == NO_RECORD); + /** + * @tc.steps: step3. call MoveToPosition interface to move to position 0. + * @tc.expected: step3. return false. + */ + EXPECT_TRUE(resultSet->MoveToPosition(CURSOR_POSITION_0) == false); + /** + * @tc.steps: step4. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step4. GetPosition returns -1, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_0); + EXPECT_EQ(resultSet->GetEntry(entryGot), NOT_FOUND); + /** + * @tc.steps: step5. call MoveToPosition interface to move to position 2. + * @tc.expected: step5. return false. + */ + EXPECT_TRUE(resultSet->MoveToPosition(CURSOR_POSITION_2) == false); + /** + * @tc.steps: step6. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step6. GetPosition returns 0, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_0); + EXPECT_EQ(resultSet->GetEntry(entryGot), NOT_FOUND); + /** + * @tc.steps: step7. call MoveToPosition interface to move to position -5. + * @tc.expected: step7. return false. + */ + EXPECT_TRUE(resultSet->MoveToPosition(CURSOR_POSITION_NEGATIVE5) == false); + /** + * @tc.steps: step8. call GetPosition interface to check the current position and GetEntry to check the Entry. + * @tc.expected: step8. GetPosition returns -1, and GetEntry returns NOT_FOUND. + */ + EXPECT_TRUE(resultSet->GetPosition() == CURSOR_POSITION_NEGATIVE1); + EXPECT_EQ(resultSet->GetEntry(entryGot), NOT_FOUND); + + /** + * @tc.steps: step11. close KvStoreResultSet. + * @tc.expected: step11. close success. + */ + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb017(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entries; + vector allKey; + GenerateRecords(ONE_HUNDRED_RECORDS, DEFAULT_START, allKey, entries); + for (const auto &iter : entries) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call CloseResultSet interface with nullptr parameter. + * @tc.expected: step2. return INVALID_ARGS. + */ + KvStoreResultSet *resultSetAllptr = nullptr; + EXPECT_EQ(delegate->CloseResultSet(resultSetAllptr), INVALID_ARGS); + /** + * @tc.steps: step3. call CloseResultSet interface with resultSetAll. + * @tc.expected: step3. return OK. + */ + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb018(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entriesKA, entriesKB; + vector allKeysKA, allKeysKB; + std::vector ka = { 'k', 'a' }; + std::vector kb = { 'k', 'b' }; + GenerateRecords(ONE_HUNDRED_RECORDS, DEFAULT_START, allKeysKA, entriesKA, ka); + GenerateRecords(ONE_HUNDRED_RECORDS, DEFAULT_START, allKeysKB, entriesKB, kb); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_EQ(delegate->Put(entriesKA[index].key, entriesKA[index].value), OK); + EXPECT_EQ(delegate->Put(entriesKB[index].key, entriesKB[index].value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetEntries interface with "ka" parameter to get KvStoreResultSet. + * @tc.expected: step2. get success. + */ + KvStoreResultSet *resultSetKA = nullptr; + Key keyPrefixKA = ka; + EXPECT_EQ(delegate->GetEntries(keyPrefixKA, resultSetKA), OK); + /** + * @tc.steps: step3. call GetEntries interface with "kb" parameter to get KvStoreResultSet. + * @tc.expected: step3. get success. + */ + KvStoreResultSet *resultSetKB = nullptr; + Key keyPrefixKB = kb; + EXPECT_EQ(delegate->GetEntries(keyPrefixKB, resultSetKB), OK); + /** + * @tc.steps: step4. call GetCount interface of all recordsets. + * @tc.expected: step4. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == TWO_HUNDREDS_RECORDS); + EXPECT_TRUE(resultSetKA->GetCount() == ONE_HUNDRED_RECORDS); + EXPECT_TRUE(resultSetKB->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step5. close resultSetAll. + * @tc.expected: step5. call success. + */ + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); + /** + * @tc.steps: step6. close resultSetAll. + * @tc.expected: step6. call success. + */ + EXPECT_TRUE(resultSetKA->GetCount() == ONE_HUNDRED_RECORDS); + EXPECT_TRUE(resultSetKB->GetCount() == ONE_HUNDRED_RECORDS); + + EXPECT_EQ(delegate->CloseResultSet(resultSetKA), OK); + EXPECT_EQ(delegate->CloseResultSet(resultSetKB), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb019(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, TEN_RECORDS, FOUR_BYTE_KEY, ONE_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetCount interface of resultSetAll and delete k1~k5. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == TEN_RECORDS); + for (unsigned int delCnt = 0; delCnt < FIVE_RECORDS; ++delCnt) { + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, entriesBatch[0].key), OK); + entriesBatch.erase(entriesBatch.begin()); + } + /** + * @tc.steps: step3. update k6 and insert another 10 * 1M records. + * @tc.expected: step3. call success. + */ + entriesBatch[0].value.push_back('a'); + EXPECT_EQ(delegate->Put(entriesBatch[0].key, entriesBatch[0].value), OK); + vector entriesBatch2; + vector allKeys2; + GenerateFixedRecords(entriesBatch2, allKeys2, TEN_RECORDS, KEY_SIX_BYTE, ONE_M_LONG_STRING); + for (const auto &iter : entriesBatch2) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step4. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step4. get success. + */ + KvStoreResultSet *resultSetAll2 = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll2), OK); + /** + * @tc.steps: step5. close resultSetAll. + * @tc.expected: step5. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == TEN_RECORDS); + EXPECT_TRUE(resultSetAll2->GetCount() == FIFTEEN_RECORDS); + + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); + EXPECT_EQ(delegate->CloseResultSet(resultSetAll2), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb020(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entries; + vector allKey; + GenerateRecords(ONE_HUNDRED_RECORDS, DEFAULT_START, allKey, entries); + for (const auto &iter : entries) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetEntries interface with "ka" parameter to get KvStoreResultSet. + * @tc.expected: step2. get success. + */ + KvStoreResultSet *resultSetKA = nullptr; + std::vector ka = { 'k', 'a' }; + Key keyPrefixKA = ka; + EXPECT_EQ(delegate->GetEntries(keyPrefixKA, resultSetKA), OK); + /** + * @tc.steps: step3. call GetEntries interface with "kb" parameter to get KvStoreResultSet. + * @tc.expected: step3. get success. + */ + KvStoreResultSet *resultSetKB = nullptr; + std::vector kb = { 'k', 'b' }; + Key keyPrefixKB = kb; + EXPECT_EQ(delegate->GetEntries(keyPrefixKB, resultSetKB), OK); + /** + * @tc.steps: step3. call GetEntries interface with "kc" parameter to get KvStoreResultSet. + * @tc.expected: step3. get success. + */ + KvStoreResultSet *resultSetKC = nullptr; + std::vector kc = { 'k', 'c' }; + Key keyPrefixKC = kc; + EXPECT_EQ(delegate->GetEntries(keyPrefixKC, resultSetKC), OK); + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll2 = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll2), OVER_MAX_LIMITS); + + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); + EXPECT_EQ(delegate->CloseResultSet(resultSetKA), OK); + EXPECT_EQ(delegate->CloseResultSet(resultSetKB), OK); + EXPECT_EQ(delegate->CloseResultSet(resultSetKC), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb021(KvStoreNbDelegate *delegate, + KvStoreDelegateManager *manager, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr && manager != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entries; + vector allKey; + GenerateRecords(ONE_HUNDRED_RECORDS, DEFAULT_START, allKey, entries); + for (const auto &iter : entries) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetCount interface of resultSetAll. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. closeKvStore returns BUSY because resultSet was not closed. + * @tc.expected: step3. call success. + */ + EXPECT_EQ(manager->CloseKvStore(delegate), BUSY); + + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); +} + +void DistributedNbCursorTestcase::ResultSetDb022(bool isRowIdMode) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *delegate = nullptr; + Option option; + option.isEncryptedDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, FOUR_BYTE_KEY, ONE_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_TRUE(delegate->GetEntries(KEY_EMPTY, resultSetAll) == OK); + /** + * @tc.steps: step2. call GetCount interface of resultSetAll. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. Rekey with g_passwd1. + * @tc.expected: step3. call success. + */ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + EXPECT_EQ(delegate->Rekey(g_passwd1), BUSY); + + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} + +void DistributedNbCursorTestcase::ResultSetDb023(bool isRowIdMode) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *delegate = nullptr; + Option option; + option.isEncryptedDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, FOUR_RECORDS, FOUR_BYTE_KEY, ONE_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetCount interface of resultSetAll. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == FOUR_RECORDS); + /** + * @tc.steps: step3. Rekey with g_passwd1. + * @tc.expected: step3. return BUSY. + */ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + EXPECT_EQ(delegate->Rekey(g_passwd1), BUSY); + + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete manager; + manager = nullptr; + + Option option2; + option2.isEncryptedDb = true; + option2.passwd = PASSWD_VECTOR_1; + option2.createIfNecessary = IS_NOT_NEED_CREATE; + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, option2); + ASSERT_TRUE(manager2 == nullptr && delegate2 == nullptr); + + option2.isEncryptedDb = false; + delegate2 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager2, g_dbParameter2, option2); + ASSERT_TRUE(manager2 != nullptr && delegate2 != nullptr); + EXPECT_EQ(manager2->CloseKvStore(delegate2), OK); + delegate2 = nullptr; + EXPECT_EQ(manager2->DeleteKvStore(STORE_ID_2), OK); + delete manager2; + manager2 = nullptr; +} + +void DistributedNbCursorTestcase::ResultSetDb024(bool isRowIdMode) +{ + KvStoreDelegateManager *manager = nullptr; + KvStoreNbDelegate *delegate = nullptr; + Option option; + option.isEncryptedDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, FOUR_BYTE_KEY, FOUR_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + /** + * @tc.steps: step1. Rekey STORE_ID_SYNC_2 with g_passwd1. + * @tc.expected: step1. operate successfully or BUSY(if the rekey is later executed). + */ + std::mutex mtx; + std::condition_variable conditionRekeyVar; + bool rekeyFlag = false; + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + thread subThread([&]() { + auto status = delegate->Rekey(g_passwd1); + EXPECT_EQ(((status == OK) || (status == BUSY)), true); + std::unique_lock lck(mtx); + conditionRekeyVar.notify_all(); + rekeyFlag = true; + }); + subThread.detach(); + /** + * @tc.steps: step2. call GetEntries interface to get KvStoreResultSet. + * @tc.expected: step2. return BUSY or OK(if the rekey is later executed). + */ + KvStoreResultSet *resultSetK = nullptr; + Key keyPrefix = { 'k' }; + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_OBSERVER_REKEY)); // wait the rekey operation. + DBStatus status = delegate->GetEntries(keyPrefix, resultSetK); + EXPECT_EQ(((status == OK) || (status == BUSY)), true); + + std::unique_lock lck(mtx); + conditionRekeyVar.wait(lck, [&] { return rekeyFlag; }); + if (resultSetK != nullptr) { + delegate->CloseResultSet(resultSetK); + } + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} +namespace { +void CursorOperThread(KvStoreNbDelegate *&nbCursorDelegate) +{ + ASSERT_TRUE(nbCursorDelegate != nullptr); + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(nbCursorDelegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSetAll->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. call IsFirst interface. + * @tc.expected: step3. call success. + */ + EXPECT_TRUE(resultSetAll->IsFirst() == false); + /** + * @tc.steps: step4. call IsLast interface. + * @tc.expected: step4. call success. + */ + EXPECT_TRUE(resultSetAll->IsLast() == false); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. call success. + */ + EXPECT_TRUE(resultSetAll->MoveToFirst() == true); + /** + * @tc.steps: step6. call MoveToLast interface. + * @tc.expected: step6. call success. + */ + EXPECT_TRUE(resultSetAll->MoveToLast() == true); + /** + * @tc.steps: step7. call IsBeforeFirst interface. + * @tc.expected: step7. call success. + */ + EXPECT_TRUE(resultSetAll->IsBeforeFirst() == false); + /** + * @tc.steps: step8. call MoveToNext interface. + * @tc.expected: step8. call success. + */ + EXPECT_TRUE(resultSetAll->MoveToNext() == false); + /** + * @tc.steps: step9. call IsAfterLast interface. + * @tc.expected: step9. call success. + */ + EXPECT_TRUE(resultSetAll->IsAfterLast() == true); + /** + * @tc.steps: step10. call MoveToPrevious and then call IsAfterLast interface. + * @tc.expected: step10. call success. + */ + EXPECT_TRUE(resultSetAll->MoveToPrevious() == true); + EXPECT_TRUE(resultSetAll->IsAfterLast() == false); + /** + * @tc.steps: step11. close recordset. + * @tc.expected: step11. call success. + */ + EXPECT_EQ(nbCursorDelegate->CloseResultSet(resultSetAll), OK); +} +} +void DistributedNbCursorTestcase::ResultSetDb025(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, + FOUR_BYTE_KEY, ONE_TENTH_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + + /** + * @tc.steps: step1. Call resultSet interfaces. + * @tc.expected: step1. operate successfully. + */ + std::vector threads; + for (unsigned int threadId = THREAD_NUM_START; threadId <= THREAD_NUM_END; ++threadId) { + threads.push_back(std::thread(CursorOperThread, std::ref(delegate))); + } + for (auto& th : threads) { + th.join(); + } +} +namespace { +void CursorRandOperThread1(KvStoreResultSet *&resultSet) +{ + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. call IsFirst interface. + * @tc.expected: step3. no crash. + */ + resultSet->IsFirst(); + /** + * @tc.steps: step4. call IsLast interface. + * @tc.expected: step4. call success. + */ + resultSet->IsLast(); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. call success. + */ + resultSet->MoveToFirst(); + /** + * @tc.steps: step6. call MoveToLast interface. + * @tc.expected: step6. call success. + */ + resultSet->MoveToLast(); + /** + * @tc.steps: step7. call IsBeforeFirst interface. + * @tc.expected: step7. call success. + */ + resultSet->IsBeforeFirst(); + /** + * @tc.steps: step8. call MoveToNext interface. + * @tc.expected: step8. call success. + */ + resultSet->MoveToNext(); + /** + * @tc.steps: step9. call IsAfterLast interface. + * @tc.expected: step9. call success. + */ + resultSet->IsAfterLast(); + /** + * @tc.steps: step10. call MoveToPrevious and then call IsAfterLast interface. + * @tc.expected: step10. call success. + */ + resultSet->MoveToPrevious(); + resultSet->IsAfterLast(); +} + +void CursorRandOperThread2(KvStoreResultSet *&resultSet) +{ + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. call IsFirst interface. + * @tc.expected: step3. call success. + */ + resultSet->IsFirst(); + /** + * @tc.steps: step7. call IsBeforeFirst interface. + * @tc.expected: step7. call success. + */ + resultSet->IsBeforeFirst(); + /** + * @tc.steps: step8. call MoveToNext interface. + * @tc.expected: step8. call success. + */ + resultSet->MoveToNext(); + /** + * @tc.steps: step9. call IsAfterLast interface. + * @tc.expected: step9. call success. + */ + resultSet->IsAfterLast(); + /** + * @tc.steps: step4. call IsLast interface. + * @tc.expected: step4. call success. + */ + resultSet->IsLast(); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. call success. + */ + resultSet->MoveToFirst(); + /** + * @tc.steps: step6. call MoveToLast interface. + * @tc.expected: step6. call success. + */ + resultSet->MoveToLast(); + /** + * @tc.steps: step10. call MoveToPrevious and then call IsAfterLast interface. + * @tc.expected: step10. call success. + */ + resultSet->MoveToPrevious(); + resultSet->IsAfterLast(); +} + +void CursorRandOperThread3(KvStoreResultSet *&resultSet) +{ + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); + /** + * @tc.steps: step3. call IsFirst interface. + * @tc.expected: step3. call success. + */ + resultSet->IsFirst(); + /** + * @tc.steps: step6. call MoveToLast interface. + * @tc.expected: step6. call success. + */ + resultSet->MoveToLast(); + /** + * @tc.steps: step7. call IsBeforeFirst interface. + * @tc.expected: step7. call success. + */ + resultSet->IsBeforeFirst(); + /** + * @tc.steps: step4. call IsLast interface. + * @tc.expected: step4. call success. + */ + resultSet->IsLast(); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. call success. + */ + resultSet->MoveToFirst(); + /** + * @tc.steps: step8. call MoveToNext interface. + * @tc.expected: step8. call success. + */ + resultSet->MoveToNext(); + /** + * @tc.steps: step9. call IsAfterLast interface. + * @tc.expected: step9. call success. + */ + resultSet->IsAfterLast(); + /** + * @tc.steps: step10. call MoveToPrevious and then call IsAfterLast interface. + * @tc.expected: step10. call success. + */ + resultSet->MoveToPrevious(); + resultSet->IsAfterLast(); +} + +void CursorRandOperThread4(KvStoreResultSet *&resultSet) +{ + /** + * @tc.steps: step10. call MoveToPrevious and then call IsAfterLast interface. + * @tc.expected: step10. call success. + */ + resultSet->IsAfterLast(); + resultSet->MoveToPrevious(); + /** + * @tc.steps: step9. call IsAfterLast interface. + * @tc.expected: step9. call success. + */ + resultSet->IsAfterLast(); + /** + * @tc.steps: step8. call MoveToNext interface. + * @tc.expected: step8. call success. + */ + resultSet->MoveToNext(); + /** + * @tc.steps: step7. call IsBeforeFirst interface. + * @tc.expected: step7. call success. + */ + resultSet->IsBeforeFirst(); + /** + * @tc.steps: step6. call MoveToLast interface. + * @tc.expected: step6. call success. + */ + resultSet->MoveToLast(); + /** + * @tc.steps: step5. call MoveToFirst interface. + * @tc.expected: step5. call success. + */ + resultSet->MoveToFirst(); + /** + * @tc.steps: step4. call IsLast interface. + * @tc.expected: step4. call success. + */ + resultSet->IsLast(); + /** + * @tc.steps: step3. call IsFirst interface. + * @tc.expected: step3. call success. + */ + resultSet->IsFirst(); + /** + * @tc.steps: step2. call GetCount interface. + * @tc.expected: step2. call success. + */ + EXPECT_TRUE(resultSet->GetCount() == ONE_HUNDRED_RECORDS); +} +} + +void DistributedNbCursorTestcase::ResultSetDb026(KvStoreNbDelegate *delegate, bool isRowIdMode) +{ + ASSERT_TRUE(delegate != nullptr); + SetResultSetCacheMode(delegate, isRowIdMode); + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, ONE_HUNDRED_RECORDS, + FOUR_BYTE_KEY, ONE_TENTH_M_LONG_STRING); + for (const auto &iter : entriesBatch) { + EXPECT_EQ(delegate->Put(iter.key, iter.value), OK); + } + + /** + * @tc.steps: step1. call GetEntries interface with "" parameter to get KvStoreResultSet. + * @tc.expected: step1. get success. + */ + KvStoreResultSet *resultSetAll = nullptr; + EXPECT_EQ(delegate->GetEntries(KEY_EMPTY, resultSetAll), OK); + + /** + * @tc.steps: step2. Call resultSet interfaces. + * @tc.expected: step2. operate successfully. + */ + std::vector threads; + threads.push_back(std::thread(CursorRandOperThread1, std::ref(resultSetAll))); + threads.push_back(std::thread(CursorRandOperThread2, std::ref(resultSetAll))); + threads.push_back(std::thread(CursorRandOperThread3, std::ref(resultSetAll))); + threads.push_back(std::thread(CursorRandOperThread4, std::ref(resultSetAll))); + + for (auto& th : threads) { + th.join(); + } + + /** + * @tc.steps: step11. close recordset. + * @tc.expected: step11. call success. + */ + EXPECT_EQ(delegate->CloseResultSet(resultSetAll), OK); +} +namespace { +void VerifyResultSetInterfaces(KvStoreNbDelegate **delegates, unsigned long delegateCount) +{ + ASSERT_TRUE(delegates != nullptr); + Key keyPrefixKA = { 'k', 'a' }; + Key keyPrefixKB = { 'k', 'b' }; + Key keyPrefixKK = { 'k', 'k' }; + KvStoreResultSet *resultSetAlls[OPEN_DB_TIMES] = { nullptr }; + KvStoreResultSet *resultSetKAs[OPEN_DB_TIMES] = { nullptr }; + KvStoreResultSet *resultSetKBs[OPEN_DB_TIMES] = { nullptr }; + KvStoreResultSet *resultSetKKs[OPEN_DB_TIMES] = { nullptr }; + unsigned long delegateCnt = 0; + for (delegateCnt = 0; delegateCnt < delegateCount; ++delegateCnt) { + /** + * @tc.steps: step2. check GetEntries of interfaces of every delegate. + * @tc.expected: step2. success. + */ + EXPECT_EQ(delegates[delegateCnt]->GetEntries(KEY_EMPTY, resultSetAlls[delegateCnt]), OK); + EXPECT_EQ(delegates[delegateCnt]->GetEntries(keyPrefixKA, resultSetKAs[delegateCnt]), OK); + EXPECT_EQ(delegates[delegateCnt]->GetEntries(keyPrefixKB, resultSetKBs[delegateCnt]), OK); + EXPECT_EQ(delegates[delegateCnt]->GetEntries(keyPrefixKK, resultSetKKs[delegateCnt]), OK); + /** + * @tc.steps: step3. check GetCount of interfaces of every delegate. + * @tc.expected: step3. success. + */ + EXPECT_TRUE(resultSetAlls[delegateCnt]->GetCount() == TWO_HUNDREDS_RECORDS); + EXPECT_TRUE(resultSetKAs[delegateCnt]->GetCount() == ONE_HUNDRED_RECORDS); + EXPECT_TRUE(resultSetKBs[delegateCnt]->GetCount() == ONE_HUNDRED_RECORDS); + EXPECT_TRUE(resultSetKKs[delegateCnt]->GetCount() == 0); + } + + for (delegateCnt = 0; delegateCnt < delegateCount; ++delegateCnt) { + /** + * @tc.steps: step4. check GetCount of interfaces of every delegate. + * @tc.expected: step4. success. + */ + EXPECT_TRUE(delegates[delegateCnt]->CloseResultSet(resultSetAlls[delegateCnt]) == OK); + EXPECT_TRUE(delegates[delegateCnt]->CloseResultSet(resultSetKAs[delegateCnt]) == OK); + EXPECT_TRUE(delegates[delegateCnt]->CloseResultSet(resultSetKBs[delegateCnt]) == OK); + EXPECT_TRUE(delegates[delegateCnt]->CloseResultSet(resultSetKKs[delegateCnt]) == OK); + } +} +} + +void DistributedNbCursorTestcase::ResultSetDb027(bool isRowIdMode) +{ + KvStoreDelegateManager *managers[OPEN_DB_TIMES] = {nullptr}; + KvStoreNbDelegate *delegates[OPEN_DB_TIMES] = {nullptr}; + Option option; + unsigned long delegateCnt = 0; + /** + * @tc.steps: step1. open STORE_ID_2 for three times. + * @tc.expected: step1. success. + */ + delegates[INDEX_ZEROTH] = DistributedDBNbTestTools::GetNbDelegateSuccess(managers[INDEX_ZEROTH], + g_dbParameter3, option); + ASSERT_TRUE(managers[INDEX_ZEROTH] != nullptr && delegates[INDEX_ZEROTH] != nullptr); + SetResultSetCacheMode(delegates[INDEX_ZEROTH], isRowIdMode); + + vector entriesKA, entriesKB; + vector allKeysKA, allKeysKB; + std::vector ka = { 'k', 'a' }; + std::vector kb = { 'k', 'b' }; + EntrySize entrySizeKA = { FIVE_BYTE_KEY, ONE_K_LONG_STRING }; + EntrySize entrySizeKB = { FIVE_BYTE_KEY, ONE_TENTH_M_LONG_STRING }; + GenerateAppointPrefixAndSizeRecords(entriesKA, entrySizeKA, ONE_HUNDRED_RECORDS, ka, {'v'}); + GenerateAppointPrefixAndSizeRecords(entriesKB, entrySizeKB, ONE_HUNDRED_RECORDS, kb, {'v'}); + for (int index = 0; index < ONE_HUNDRED_RECORDS; index++) { + EXPECT_TRUE(delegates[INDEX_ZEROTH]->Put(entriesKA[index].key, entriesKA[index].value) == OK); + EXPECT_TRUE(delegates[INDEX_ZEROTH]->Put(entriesKB[index].key, entriesKB[index].value) == OK); + } + + option.createIfNecessary = false; + for (delegateCnt = INDEX_FIRST; delegateCnt < DELEGATE_NUM; ++delegateCnt) { + delegates[delegateCnt] = DistributedDBNbTestTools::GetNbDelegateSuccess(managers[delegateCnt], + g_dbParameter3, option); + ASSERT_TRUE(managers[delegateCnt] != nullptr && delegates[delegateCnt] != nullptr); + SetResultSetCacheMode(delegates[delegateCnt], isRowIdMode); + } + + VerifyResultSetInterfaces(delegates, DELEGATE_NUM); + for (unsigned long operCnt = INDEX_ZEROTH; operCnt < OPEN_DB_TIMES; ++operCnt) { + EXPECT_EQ(managers[operCnt]->CloseKvStore(delegates[operCnt]), OK); + delegates[operCnt] = nullptr; + if (operCnt == OPEN_DB_TIMES - 1) { + EXPECT_EQ(managers[operCnt]->DeleteKvStore(STORE_ID_3), OK); + } + delete managers[operCnt]; + managers[operCnt] = nullptr; + } +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_enable_sync_by_closed_db_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_enable_sync_by_closed_db_test.cpp new file mode 100755 index 000000000..7d8a6fedc --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_enable_sync_by_closed_db_test.cpp @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "distributeddb_schema_test_tools.h" +#include "process_communicator_test_stub.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbEnableSyncByClosedDb { +const int PARAM_CRITICAL_LENGTH = 128; +const int STRING_ONE_BYTE = 1; +const int STRING_TEN_BYTE = 10; +const int STRING_ONE_TWO_EIGHT_BYTE = 128; +const int STRING_ONE_TWO_NINE_BYTE = 129; + +class DistributeddbNbEnableSyncByClosedDbTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbEnableSyncByClosedDbTest::SetUpTestCase(void) +{ + SetDir(NB_DIRECTOR); +} + +void DistributeddbNbEnableSyncByClosedDbTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbEnableSyncByClosedDbTest::SetUp(void) +{ + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbNbEnableSyncByClosedDbTest::TearDown(void) +{ +} +#ifndef OMIT_JSON +/* + * @tc.name: ParamCheck 001 + * @tc.desc: test EnableKvStoreAutoLaunch and DisableKvStoreAutoLaunch interface checking 3 elements function. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck001, TestSize.Level0) +{ + string schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + AutoLaunchOption option = {true, false, CipherType::DEFAULT, NULL_PASSWD, schema, true, NB_DIRECTOR, nullptr}; + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch and DisableKvStoreAutoLaunch interface using the storeId such as + * "", or string with "\0", or length of the storeId is 128\129, or that has Uppercase letters, Lowercase + * letters, digit "_", or some Special characters. + * @tc.expected: step1. only the storeId has "\0", or length is 128, or has Upper or lower letters or digit or "_" + * can enable success, otherwise it will return INVALID_ARGS. + */ + string storeId1(PARAM_CRITICAL_LENGTH, 'a'); + vector storeIdValid = {storeId1, "abc\0", "Abc576_"}; + for (unsigned long index = 0; index < storeIdValid.size(); index++) { + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, storeIdValid[index], option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, storeIdValid[index]), OK); + EXPECT_EQ(manager->DeleteKvStore(storeIdValid[index]), OK); + } + + string storeId2(PARAM_CRITICAL_LENGTH + 1, 'a'); + vector storeIdInValid = {"", "6\\", "6//", "6&", "6^", "6%", "6#", "6-", "6中文", "6Ää", storeId2}; + for (unsigned long index = 0; index < storeIdInValid.size(); index++) { + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, storeIdInValid[index], option, nullptr), + INVALID_ARGS); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, storeIdInValid[index]), NOT_FOUND); + } + + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch and DisableKvStoreAutoLaunch interface using the appId such as + * "", or length of the storeId is 128\129. + * @tc.expected: step2. only the storeId has the length as 128 + * can enable success, otherwise it will return INVALID_ARGS. + */ + string appId1(PARAM_CRITICAL_LENGTH, 'b'); + string appId2(PARAM_CRITICAL_LENGTH + 1, 'b'); + vector appId = {"", appId1, appId2}; + DBStatus result[] = {INVALID_ARGS, OK, INVALID_ARGS}; + DBStatus resultDisable[] = {NOT_FOUND, OK, NOT_FOUND}; + for (unsigned long index = 0; index < appId.size(); index++) { + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, appId[index], STORE_ID_1, option, nullptr), + result[index]); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, appId[index], STORE_ID_1), resultDisable[index]); + } + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + + /** + * @tc.steps: step3. call EnableKvStoreAutoLaunch and DisableKvStoreAutoLaunch interface using the userId such as + * "", or length of the storeId is 128\129. + * @tc.expected: step3. only the storeId has the length as 128 + * can enable success, otherwise it will return INVALID_ARGS. + */ + string userId1(PARAM_CRITICAL_LENGTH, 'c'); + string userId2(PARAM_CRITICAL_LENGTH + 1, 'c'); + vector userId = {"", appId1, appId2}; + for (unsigned long index = 0; index < userId.size(); index++) { + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(userId[index], APP_ID_1, STORE_ID_1, option, nullptr), + result[index]); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(userId[index], APP_ID_1, STORE_ID_1), resultDisable[index]); + } + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + + delete manager; + manager = nullptr; +} +#endif +/* + * @tc.name: ParamCheck 002 + * @tc.desc: test EnableKvStoreAutoLaunch can only effect when option.createIfNecessary=true if the DB is not exist. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck002, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * dataDir = NB_DIRECTOR, observer = TheAppoitObserver; + * @tc.expected: step1. enable failed, and return DB_ERROR. + */ + KvStoreObserverImpl *observer = nullptr; + AutoLaunchOption option; + option.createIfNecessary = false; + option.dataDir = NB_DIRECTOR; + option.observer = observer; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), DB_ERROR); + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * and other settings don't change; + * @tc.expected: step2. enable succeed, and return OK. + */ + option.createIfNecessary = true; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + + /** + * @tc.steps: step3. create db use option.createIfNecessary = false, and put (k1, v1), (k2, v2); + * @tc.expected: step3. create and put succeed. + */ + Option dbOption; + dbOption.createIfNecessary = false; + dbOption.isMemoryDb = false; + KvStoreNbDelegate *delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, dbOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + EXPECT_EQ(delegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(delegate->Put(KEY_2, VALUE_2), OK); + + /** + * @tc.steps: step4. update (k1, v1) to (k1, v2), and check the data with Get interface, and then delete (k2, v2); + * @tc.expected: step4. operate succeed. + */ + EXPECT_EQ(delegate->Put(KEY_1, VALUE_2), OK); + Value realValue; + EXPECT_EQ(delegate->Get(KEY_1, realValue), OK); + EXPECT_EQ(realValue, VALUE_2); + EXPECT_EQ(delegate->Delete(KEY_2), OK); + + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ParamCheck 003 + * @tc.desc: verification of isEncrypted, cipher, passwd of AutoLaunchOption. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck003, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = true, but the passwd is ""; + * @tc.expected: step1. enable failed, and return INVALID_ARGS. + */ + AutoLaunchOption option = {true, true, CipherType::DEFAULT, NULL_PASSWD, "", true, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), INVALID_ARGS); + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = true, and the passwd is 100 length; + * @tc.expected: step2. enable succeed, and return OK. + */ + CipherPassword passwd1, passwd2, passwd3; + vector passwordVector1(ENCRYPT_COUNT, 'a'); + passwd1.SetValue(passwordVector1.data(), ENCRYPT_COUNT); + option.passwd = passwd1; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + + /** + * @tc.steps: step3. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = true, the passwd is 128 length; + * @tc.expected: step3. enable succeed, and return OK. + */ + vector passwordVector2(PARAM_CRITICAL_LENGTH, 'a'); + passwd2.SetValue(passwordVector2.data(), PARAM_CRITICAL_LENGTH); + option.passwd = passwd2; + option.cipher = CipherType::AES_256_GCM; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + /** + * @tc.steps: step4. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = true, the passwd is 129 length; + * @tc.expected: step4. enable failed, and return INVALID_ARGS. + */ + vector passwordVector3(PASSWD_BYTE, 'a'); + passwd3.SetValue(passwordVector3.data(), PASSWD_BYTE); + option.passwd = passwd3; + option.cipher = CipherType::DEFAULT; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), INVALID_ARGS); + + /** + * @tc.steps: step5. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = false, the passwd is 129 length; + * @tc.expected: step5. enable success, and return OK. + */ + option.isEncryptedDb = false; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} +#ifndef OMIT_JSON +/* + * @tc.name: ParamCheck 004 + * @tc.desc: verify that isEncrypted, passwd of AutoLaunchOption must be the same to params of the db created already. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck004, TestSize.Level0) +{ + /** + * @tc.steps: step1. create db with the option isEncryptedDb = true, passwd = PASSWD_VECTOR_1; + * @tc.expected: step1. create successfully. + */ + string schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + KvStoreDelegateManager *manager = nullptr; + Option dbOption; + dbOption.isEncryptedDb = true; + dbOption.passwd = PASSWD_VECTOR_1; + dbOption.schema = schema; + dbOption.isMemoryDb = false; + KvStoreNbDelegate *delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, dbOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * isEncryptedDb = false, the passwd is passwd1 and a valid schema; + * @tc.expected: step2. enable failed, and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + CipherPassword passwd1, passwd2; + passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + AutoLaunchOption option = {false, false, CipherType::DEFAULT, passwd1, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), + INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step3. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * isEncryptedDb = true, and the passwd is passwd2; + * @tc.expected: step3. enable failed, and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + option = {false, true, CipherType::DEFAULT, passwd2, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), + INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step4. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * isEncryptedDb = true, and the passwd is passwd1; + * @tc.expected: step4. enable success, and return OK. + */ + option = {false, true, CipherType::DEFAULT, passwd1, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_1, dbOption.isMemoryDb)); + + /** + * @tc.steps: step5. create unEncrypted DB2, but passwd = p1; + * @tc.expected: step5. create success. + */ + dbOption.isEncryptedDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, dbOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step6. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * isEncryptedDb = true, and the passwd is passwd2; + * @tc.expected: step6. enable failed, and return INVALID_PASSWD_OR_CORRUPTED_DB. + */ + passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + option = {false, true, CipherType::DEFAULT, passwd1, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_2, APP_ID_2, STORE_ID_2, option, nullptr), + INVALID_PASSWD_OR_CORRUPTED_DB); + + /** + * @tc.steps: step7. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false, + * isEncryptedDb = false, and the passwd is passwd2; + * @tc.expected: step7. enable success, and return OK. + */ + passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); + option = {false, false, CipherType::DEFAULT, passwd2, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_2, APP_ID_2, STORE_ID_2, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_2, APP_ID_2, STORE_ID_2), OK); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, dbOption.isMemoryDb)); +} + +/* + * @tc.name: ParamCheck 005 + * @tc.desc: verify that EnableKvStoreAutoLaunch interface would check the schema param's legitimacy. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck005, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = false, and a valid schema; + * @tc.expected: step1. enable success, and return OK. + */ + string schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + AutoLaunchOption option = {true, false, CipherType::DEFAULT, NULL_PASSWD, schema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = false, and a invalid schema; + * @tc.expected: step2. enable failed, and return INVALID_SCHEMA. + */ + string inValidSchema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, INVALID_DEFINE_2, VALID_INDEX_1); + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, inValidSchema, false, NB_DIRECTOR, nullptr}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr), + INVALID_SCHEMA); + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ParamCheck 006 + * @tc.desc: verify that EnableKvStoreAutoLaunch interface would check the dataDir param's legitimacy. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck006, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary is true, + * isEncryptedDb = false, and different dataDir that include many special characters; + * @tc.expected: step1. enable success, and return OK. + */ + string middlePath = "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd/" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/" + "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg/"; + // the length of the valid string is 504-102 = 402 + string tailPathValid(402 - (NB_DIRECTOR + middlePath).length(), 'e'); + // the length of the invalid string is 504-101 = 403 + string tailPathInvalid(403 - (NB_DIRECTOR + middlePath).length(), 'h'); + string validLengthDir = NB_DIRECTOR + middlePath + tailPathValid; + string inValidLengthDir = NB_DIRECTOR + middlePath + tailPathInvalid; + + vector dirs = {"", NB_DIRECTOR + "a\0", validLengthDir, inValidLengthDir, + NB_DIRECTOR + "a..b", NB_DIRECTOR + "a/…b", + NB_DIRECTOR + "a/中文", NB_DIRECTOR + "a\\//&^a%#", + NB_DIRECTOR + "diehgid/"}; + vector results = {INVALID_ARGS, OK, OK, INVALID_ARGS, OK, OK, OK, OK, OK, OK, INVALID_ARGS}; + string schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + + AutoLaunchOption option; + DBStatus status; + RemoveDir("/data/test/getstub"); + for (unsigned long index = 0; index < dirs.size(); index++) { + if (results[index] == OK) { + SetDir(dirs[index]); + } + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, schema, false, dirs[index], nullptr}; + status = manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, nullptr); + if (index == 3) { // the 3th element + EXPECT_NE(status, OK); + } else { + EXPECT_EQ(status, results[index]); + } + if (status == OK) { + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + } + if (results[index] == OK) { + RemoveDir(dirs[index]); + } + } + + delete manager; + manager = nullptr; +} + +/* + * @tc.name: ParamCheck 007 + * @tc.desc: verify that EnableKvStoreAutoLaunch many times, it can only be successfully when the option is same as the + * first time unless call DisableKvStoreAutoLaunch interface to disable it. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ParamCheck007, TestSize.Level0) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + EXPECT_EQ(manager->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + AutoLaunchCallback callback; + auto notifier = bind(&AutoLaunchCallback::AutoLaunchNotifier, &callback, placeholders::_1, placeholders::_2, + placeholders::_3, placeholders::_4); + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = false; + * @tc.expected: step1. enable success, and return OK. + */ + KvStoreObserverImpl observer; + AutoLaunchOption option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), OK); + + /** + * @tc.steps: step2. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = false. + * @tc.expected: step2. enable failed, and return ALREADY_SET. + */ + option = {false, false, CipherType::DEFAULT, NULL_PASSWD, "", false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step3. call EnableKvStoreAutoLaunch use the option with which isEncrypt = true, passwd = p1. + * @tc.expected: step3. enable failed, and return ALREADY_SET. + */ + CipherPassword passwd1; + passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + option = {true, true, CipherType::DEFAULT, passwd1, "", false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step4. call EnableKvStoreAutoLaunch use the option with which schema is a valid schema. + * @tc.expected: step4. enable failed, and return ALREADY_SET. + */ + string schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, schema, false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step5. call EnableKvStoreAutoLaunch use the option with which createDirByStoreIdOnly = true. + * @tc.expected: step5. enable failed, and return ALREADY_SET. + */ + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", true, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step6. call EnableKvStoreAutoLaunch use the option with which dataDir = DIRECTOR. + * @tc.expected: step6. enable failed, and return ALREADY_SET. + */ + SetDir(DIRECTOR); + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", false, DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step7. call EnableKvStoreAutoLaunch use the option with which observer = observer2. + * @tc.expected: step7. enable failed, and return ALREADY_SET. + */ + KvStoreObserverImpl observer2; + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", false, NB_DIRECTOR, &observer2}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + + /** + * @tc.steps: step8. call EnableKvStoreAutoLaunch use the option with which notifier is not null. + * @tc.expected: step8. enable failed, and return ALREADY_SET. + */ + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + /** + * @tc.steps: step9. call EnableKvStoreAutoLaunch use the option with the params is the same with the first time . + * @tc.expected: step9. enable failed, and return ALREADY_SET. + */ + option = {true, false, CipherType::DEFAULT, NULL_PASSWD, "", false, NB_DIRECTOR, &observer}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), ALREADY_SET); + /** + * @tc.steps: step10. call DisableKvStoreAutoLaunch interface to disable the function, + * and use the option which is different from the first time EnableKvStoreAutoLaunch used. + * @tc.expected: step10. enable success, and return OK. + */ + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + option = {true, true, CipherType::DEFAULT, passwd1, schema, true, DIRECTOR, &observer2}; + EXPECT_EQ(manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, option, notifier), OK); + EXPECT_EQ(manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1), OK); + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} +#endif +void VerifyDisableRetStatus(KvStoreDelegateManager *&manager) +{ + DBStatus status = manager->DisableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1); + EXPECT_TRUE(status == OK || status == NOT_FOUND || status == BUSY); +} +/* + * @tc.name: ClosedSyncPressure 001 + * @tc.desc: concurrency open/close operation of EnableKvStoreAutoLaunch or DisableKvStoreAutoLaunch. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, ClosedSyncPressure001, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + AutoLaunchOption autoLaunchOption; + autoLaunchOption.isEncryptedDb = option.isEncryptedDb; + autoLaunchOption.passwd.SetValue(option.passwd.data(), option.passwd.size()); + autoLaunchOption.dataDir = NB_DIRECTOR; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + /** + * @tc.steps: step1. call EnableKvStoreAutoLaunch use the option with which createIfNecessary = true, + * isEncryptedDb = false; + * @tc.expected: step1. enable success, and return OK. + */ + vector threads; + const unsigned int threeThreads = 3; + for (unsigned int threadId = 0; threadId < threeThreads; ++threadId) { + threads.push_back(thread([&]() { + DBStatus enableStatus = manager->EnableKvStoreAutoLaunch(USER_ID_1, APP_ID_1, STORE_ID_1, + autoLaunchOption, nullptr); + EXPECT_TRUE(enableStatus == OK || enableStatus == ALREADY_SET); + })); + } + + for (unsigned int threadId = 0; threadId < threeThreads; ++threadId) { + threads.push_back(thread([&]() { + VerifyDisableRetStatus(manager); + })); + } + + for (unsigned int threadId = 0; threadId <= threeThreads; ++threadId) { + threads.push_back(thread([&]() { + KvStoreNbDelegate *delegateThread = nullptr; + KvStoreDelegateManager *managerThread = nullptr; + option.createIfNecessary = false; + delegateThread = DistributedDBNbTestTools::GetNbDelegateSuccess(managerThread, g_dbParameter1, option); + ASSERT_TRUE(managerThread != nullptr && delegateThread != nullptr); + EXPECT_TRUE(managerThread->CloseKvStore(delegateThread) == OK); + delegateThread = nullptr; + delete managerThread; + managerThread = nullptr; + })); + } + + for (auto &th : threads) { + th.join(); + } + VerifyDisableRetStatus(manager); + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: SyncCommErr001 + * @tc.desc: Test Sync return Code, when Communicator get some error. + * @tc.type: FUNC + * @tc.require: SR000DR9JR + * @tc.author: xushaohua + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, SyncCommErr001, TestSize.Level1) +{ + KvStoreDelegateManager *manager = new (std::nothrow) KvStoreDelegateManager(APP_ID_1, USER_ID_1); + ASSERT_NE(manager, nullptr); + manager->SetProcessLabel("MST", "GetDevicesID"); + std::shared_ptr communicator = std::make_shared(); + manager->SetProcessCommunicator(communicator); + + /** + * @tc.steps: step1. Get a KvStoreNbDelegate delegate. + * @tc.expected: step1. delegate is not null. + */ + Option dbOption; + KvStoreNbDelegate *delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter1, dbOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step2. Set the ProcessCommunicatorTestStub CommErr to make communicator error scene, and + * call sync. + * @tc.expected: step2. Sync callback will return COMM_FAILURE. + */ + communicator->SetCommErr(true); + const std::string testDeviceName = "test_device"; + std::vector deviceList; + deviceList.push_back(testDeviceName); + + auto syncCallBack = [testDeviceName](const std::map &statusMap) { + auto iter = statusMap.find(testDeviceName); + EXPECT_NE(iter, statusMap.end()); + EXPECT_EQ(iter->second, COMM_FAILURE); + }; + EXPECT_EQ(delegate->Sync(deviceList, SYNC_MODE_PUSH_ONLY, syncCallBack, true), OK); + + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_1), OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: AutoLaunch 001 + * @tc.desc: GetKvStoreIdentifier returns the responding hashIdentity if the parameters are valid, + * or it returns empty string. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbEnableSyncByClosedDbTest, AutoLaunch001, TestSize.Level1) +{ + KvStoreDelegateManager manager(APP_ID_1, USER_ID_1); + /** + * @tc.steps: step1. call GetKvStoreIdentifier with valid useId, appId and storeId + * @tc.expected: step1. returns valid hashIdentity. + */ + string hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, STORE_ID_SYNC_1); + EXPECT_NE(hashIdentity, ""); + /** + * @tc.steps: step2. call GetKvStoreIdentifier separately with empty string, 128 or 129 characters as userId + * @tc.expected: step2. returns empty string. + */ + string userId = ""; + hashIdentity = manager.GetKvStoreIdentifier(APP_ID_1, userId, STORE_ID_SYNC_1); + EXPECT_EQ(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_EIGHT_BYTE, RandType::ALPHA_NUM, userId); + hashIdentity = manager.GetKvStoreIdentifier(APP_ID_1, userId, STORE_ID_SYNC_1); + EXPECT_NE(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_NINE_BYTE, RandType::ALPHA_NUM, userId); + hashIdentity = manager.GetKvStoreIdentifier(APP_ID_1, userId, STORE_ID_SYNC_1); + EXPECT_EQ(hashIdentity, ""); + /** + * @tc.steps: step3. call GetKvStoreIdentifier separately with empty string, 128 or 129 characters as appId + * @tc.expected: step3. returns empty string. + */ + string appId = ""; + hashIdentity = manager.GetKvStoreIdentifier(appId, USER_ID_1, STORE_ID_SYNC_1); + EXPECT_EQ(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_EIGHT_BYTE, RandType::ALPHA_NUM, appId); + hashIdentity = manager.GetKvStoreIdentifier(appId, USER_ID_1, STORE_ID_SYNC_1); + EXPECT_NE(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_NINE_BYTE, RandType::ALPHA_NUM, appId); + hashIdentity = manager.GetKvStoreIdentifier(appId, USER_ID_1, STORE_ID_SYNC_1); + EXPECT_EQ(hashIdentity, ""); + /** + * @tc.steps: step4. call GetKvStoreIdentifier separately with empty string, '\0' included characters, + * 128 or 129 characters, upper case, lower case, number, _ randomed characters, + * special characters(\\ // & ^ % # -, Chinese or Latin characters) as storeId + * @tc.expected: step4. returns empty string. + */ + string storeId = ""; + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_EQ(hashIdentity, ""); + + string prefixStoreId, suffixStoreId; + GenerateFixedLenRandString(GetRandInt(STRING_ONE_BYTE, STRING_TEN_BYTE), RandType::ALPHA_NUM, prefixStoreId); + GenerateFixedLenRandString(GetRandInt(STRING_ONE_BYTE, STRING_TEN_BYTE), RandType::ALPHA_NUM, suffixStoreId); + storeId = prefixStoreId + "\0" + suffixStoreId; + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_NE(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_EIGHT_BYTE, RandType::ALPHA_NUM, storeId); + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_NE(hashIdentity, ""); + + GenerateFixedLenRandString(STRING_ONE_TWO_NINE_BYTE, RandType::ALPHA_NUM, storeId); + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_EQ(hashIdentity, ""); + + GenerateFixedLenRandString(ONE_K_LONG_STRING, RandType::ALPHA_NUM_UNDERLINE, storeId); + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_EQ(hashIdentity, ""); + + GenerateFixedLenRandString(ONE_K_LONG_STRING, RandType::SPECIAL, storeId); + storeId += string("中文"); + storeId += string("Ää"); + hashIdentity = manager.GetKvStoreIdentifier(USER_ID_1, APP_ID_1, storeId); + EXPECT_EQ(hashIdentity, ""); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_local_batch_crud_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_local_batch_crud_test.cpp new file mode 100755 index 000000000..430d750f7 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_local_batch_crud_test.cpp @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "process_communicator_test_stub.h" +#include "distributeddb_schema_test_tools.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbLocalBatchCrud { +KvStoreNbDelegate *g_nbLocalBatchDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; + +class DistributeddbNbLocalBatchCrudTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbLocalBatchCrudTest::SetUpTestCase(void) +{ + KvStoreDelegateManager manager(APP_ID_1, USER_ID_1); + manager.SetProcessLabel("MST", "GetDevicesID"); + manager.SetProcessCommunicator(std::make_shared()); +} + +void DistributeddbNbLocalBatchCrudTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbLocalBatchCrudTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_nbLocalBatchDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, g_option); + ASSERT_TRUE(g_nbLocalBatchDelegate != nullptr); + ASSERT_TRUE(g_manager != nullptr); +} + +void DistributeddbNbLocalBatchCrudTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_nbLocalBatchDelegate, STORE_ID_1, g_option.isMemoryDb)); + RemoveDir(NB_DIRECTOR); +} + +/** + * @tc.name: SimpleData 001 + * @tc.desc: Verify that single-ver db can support PutLocalBatch to insert and update records db file, + * and DeleteLocalBatch from db. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData001, TestSize.Level0) +{ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries1.push_back(ENTRY_3); + entries1.push_back(ENTRY_4); + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_3_4); + + /** + * @tc.steps: step1. call PutLocalBatch interface to Put (k1, v1), (k2, v2), (k3, v3), (k4, v4) to DB + * and check the data in DB. + * @tc.expected: step1. PutLocalBatch successfully and there are (k1, v1), (k2, v2), (k3, v3), (k4, v4) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step2. call PutLocalBatch interface to update (k1, v1), (k3, v3) to (k1, v2), (k3, v4) + * and check the data in DB. + * @tc.expected: step2. PutLocalBatch successfully and there are (k1, v2), (k2, v2), (k3, v4), (k4, v4) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + entries1[0] = ENTRY_1_2; + entries1[2] = ENTRY_3_4; + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface to delete (k3, v4), (k4, v4) from db and check the data in DB. + * @tc.expected: step3. DeleteLocalBatch successfully and there are only (k1, v2), (k2, v2) in DB. + */ + vector keys; + keys.push_back(ENTRY_3.key); + keys.push_back(ENTRY_4.key); + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + entries1.pop_back(); + entries1.pop_back(); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); +} + +/** + * @tc.name: SimpleData 002 + * @tc.desc: Verify that single-ver db can support PutLocalBatch records that value of which is null, but can't support + * the value of which bigger than 4M. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData002, TestSize.Level0) +{ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back({.key = KEY_2, .value = {}}); + + /** + * @tc.steps: step1. call PutLocalBatch interface to Put (k1, v1), (k2, null) to DB and check the data in DB. + * @tc.expected: step1. PutLocalBatch successfully and there are (k1, v1), (k2, null) in DB. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step2. call PutLocalBatch interface put (k3, v3), (k3, v4) to db, size of v4 is 4M + 1 + * and then check the data in DB. + * @tc.expected: step2. PutLocalBatch failed and there are only (k1, v1), (k2, null) in DB. + */ + entries2.push_back(ENTRY_3); + Entry entry; + EntrySize entrySize = {KEY_ONE_K_BYTE, FOUR_M_LONG_STRING + 1}; + unsigned long keyNo = 1; + GenerateRandRecord(entry, entrySize, keyNo); + entries2.push_back(entry); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), INVALID_ARGS); + + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); +} + +/** + * @tc.name: SimpleData 003 + * @tc.desc: Verify that PutLocalBatch can't operate records key of which is null or key is bigger than 1k. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData003, TestSize.Level0) +{ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back({.key = {}, .value = VALUE_2}); + + /** + * @tc.steps: step1. call PutLocalBatch interface to Put (k1, v1), (null, v2) to DB. + * @tc.expected: step1. PutLocalBatch failed. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), INVALID_ARGS); + + /** + * @tc.steps: step2. call PutLocalBatch interface put (k3, v3), (k4, v4) to db, size of k4 is 1K + 1 + * and then check the data in DB. + * @tc.expected: step2. PutLocalBatch failed and there are only (k1, v1), (k2, null) in DB. + */ + entries2.push_back(ENTRY_3); + Entry entry; + EntrySize entrySize = {KEY_ONE_K_BYTE + 1, FOUR_M_LONG_STRING}; + unsigned long keyNo = 1; + GenerateRandRecord(entry, entrySize, keyNo); + entries2.push_back(entry); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), INVALID_ARGS); + + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 004 + * @tc.desc: Verify that DeleteLocalBatch can't operate records key of which is null or key is bigger than 1k. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData004, TestSize.Level0) +{ + /** + * @tc.steps: step1. call PutLocalBatch interface to Put (k1, v1), (k2, v2), (k3, v3), (k4, v4) to DB, and check + * the data from. + * @tc.expected: step1. PutLocalBatch succeed and can find (k1, v1), (k2, v2), (k3, v3), (k4, v4) from db. + */ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries1.push_back(ENTRY_3); + entries1.push_back(ENTRY_4); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + vector keys1, keys2; + keys1.push_back(ENTRY_1.key); + Key key(KEY_ONE_K_BYTE + 1, 'k'); + keys1.push_back(key); + + /** + * @tc.steps: step2. call DeleteLocalBatch interface to Delete k1, k5 from DB, size of k5 is 1K + 1. + * @tc.expected: step2. DeleteLocalBatch failed and all the data is still in db. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys1), INVALID_ARGS); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface delete k2, k6 from db, size of k6 is null + * and then check the data in DB. + * @tc.expected: step3. DeleteLocalBatch failed and all the data is still in db. + */ + keys2.push_back(ENTRY_2.key); + keys2.push_back({}); + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys2), INVALID_ARGS); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); +} + +/** + * @tc.name: SimpleData 005 + * @tc.desc: Verify that DeleteLocalBatch can delete records that is not exist. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData005, TestSize.Level0) +{ + /** + * @tc.steps: step1. call PutLocalBatch interface to Put (k1, v1), (k2, v2) to DB, and check the data in db. + * @tc.expected: step1. PutLocalBatch succeed and can find (k1, v1), (k2, v2) from db. + */ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + keys.push_back(KEY_3); + + /** + * @tc.steps: step2. call DeleteLocalBatch interface to Delete k1, k2, k3 from DB. + * @tc.expected: step2. DeleteLocalBatch succeed and can't find data in db. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), NOT_FOUND); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface again to Delete k1, k2, k3 from DB. + * @tc.expected: step3. DeleteLocalBatch succeed and can't find data in db. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), NOT_FOUND); +} + +/** + * @tc.name: SimpleData 006 + * @tc.desc: Verify that PutLocalBatch and DeleteLocalBatch can't operate 129 records one time. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData006, TestSize.Level0) +{ + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbLocalBatchDelegate, KEY_1, VALUE_1), OK); + /** + * @tc.steps: step1. call PutLocalBatch interface to insert 128 records to DB, and check the data from DB. + * @tc.expected: step1. PutLocalBatch succeed and can find 129 records in DB. + */ + vector entries1, entries2, gotValues; + vector allKeys1, allKeys2; + GenerateFixedRecords(entries1, allKeys1, BATCH_RECORDS, KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE); + + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_EQ(entries1.size() + 1, gotValues.size()); + + /** + * @tc.steps: step2. call PutLocalBatch interface to insert 129 records to DB, and check the data from DB. + * @tc.expected: step2. PutLocalBatch failed and can only find 129 records in DB. + */ + GenerateFixedRecords(entries2, allKeys2, BATCH_RECORDS + 1, KEY_EIGHT_BYTE, VALUE_ONE_HUNDRED_BYTE); + EXPECT_EQ(g_nbLocalBatchDelegate->PutLocalBatch(entries2), INVALID_ARGS); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_EQ(entries1.size() + 1, gotValues.size()); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface to Delete 129 records from DB which is already in DB. + * @tc.expected: step3. DeleteLocalBatch failed and can still find the 129 records which was find upstairs in db. + */ + allKeys1.push_back(KEY_1); + EXPECT_EQ(g_nbLocalBatchDelegate->DeleteLocalBatch(allKeys1), INVALID_ARGS); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_EQ(entries1.size() + 1, gotValues.size()); +} + +#ifndef LOW_LEVEL_MEM_DEV +/** + * @tc.name: SimpleData 007 + * @tc.desc: Verify that rekey will return busy when it is PutLocalBatch-ing. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData007, TestSize.Level3) +{ + /** + * @tc.steps: step1. start one new thread to use PutLocalBatch interface to insert 128 (1k, 4M) records to DB. + * @tc.expected: step1. PutLocalBatch succeed. + */ + vector entries; + EntrySize entrySize = {KEY_ONE_K_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, BATCH_RECORDS); + + std::condition_variable batchCondition; + std::mutex batchMtx; + bool putBatchFinish = false; + thread subThread([&]() { + DBStatus inserStatus = DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries); + EXPECT_TRUE(inserStatus == OK || inserStatus == BUSY); + { + std::lock_guard lock(batchMtx); + putBatchFinish = true; + } + batchCondition.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. rekey the DB when it is PutLocalBatch-ing. + * @tc.expected: step2. rekey failed and it will return busy. + */ + DistributedDB::CipherPassword passwd; + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_FIFTY_MS)); + DBStatus rekeyStatus = g_nbLocalBatchDelegate->Rekey(passwd); + EXPECT_TRUE(rekeyStatus == BUSY || rekeyStatus == OK); + + std::unique_lock lck(batchMtx); + batchCondition.wait(lck, [&] { return putBatchFinish; }); +} + +/** + * @tc.name: SimpleData 008 + * @tc.desc: Verify that import will return busy when it is PutLocalBatch-ing. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData008, TestSize.Level3) +{ + EXPECT_EQ(g_nbLocalBatchDelegate->Put(KEY_1, VALUE_1), OK); + + std::string exportPath = NB_DIRECTOR + "export"; + SetDir(exportPath); + std::string filePath = exportPath + "/bkpDB.bin"; + EXPECT_TRUE(g_nbLocalBatchDelegate->Export(filePath, NULL_PASSWD) == OK); + /** + * @tc.steps: step1. call PutLocalBatch interface to insert 128 records to DB. + * @tc.expected: step1. PutLocalBatch succeed. + */ + vector entries; + EntrySize entrySize = {KEY_ONE_K_BYTE, FOUR_M_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries, entrySize, BATCH_RECORDS); + + std::condition_variable batchCondition; + std::mutex batchMtx; + bool putBatchFinish = false; + thread subThread([&]() { + DBStatus insertStatus = DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries); + EXPECT_TRUE(insertStatus == OK || insertStatus == BUSY); + { + std::lock_guard lock(batchMtx); + putBatchFinish = true; + } + batchCondition.notify_one(); + }); + subThread.detach(); + + /** + * @tc.steps: step2. import the backup file of the db. + * @tc.expected: step2. import failed and return busy. + */ + std::this_thread::sleep_for(std::chrono::microseconds(WAIT_FOR_FIFTY_MS)); + DBStatus importStatus = g_nbLocalBatchDelegate->Import(filePath, NULL_PASSWD); + EXPECT_TRUE(importStatus == BUSY || importStatus == OK); + + std::unique_lock lck(batchMtx); + batchCondition.wait(lck, [&] { return putBatchFinish; }); +} +#endif +#ifndef OMIT_JSON +/** + * @tc.name: SimpleData 009 + * @tc.desc: Verify that open the schema db use non-schema mode, it can open success on readOnly mode and can be CRUD. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, SimpleData009, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option1 = g_option; + string schemaDefine = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + option1.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, schemaDefine, VALID_INDEX_1); + option1.isMemoryDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option1); + ASSERT_NE(delegate, nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delete manager; + manager = nullptr; + + /** + * @tc.steps: step1. open the schema db with non-schema mode, and then do PutLocal, GetLocal, DeleteLocal. + * @tc.expected: step1. open successfully and all operate successfully. + */ + Option option2 = g_option; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option2); + ASSERT_NE(delegate, nullptr); + + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*delegate, KEY_1, VALUE_1), OK); + Value gotValue; + EXPECT_EQ(delegate->GetLocal(KEY_1, gotValue), OK); + EXPECT_TRUE(CompareVector(gotValue, VALUE_1)); + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*delegate, KEY_1, VALUE_2), OK); + EXPECT_EQ(delegate->DeleteLocal(KEY_1), OK); + /** + * @tc.steps: step2. PutLocalBatch 128 records, GetLocalEntries PutLocalBatch to update it + * and then DeleteLocalBatch them. + * @tc.expected: step2. operate successfully. + */ + vector entries1, entries2, gotEntries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, BATCH_RECORDS); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*delegate, entries1), OK); + EXPECT_EQ(delegate->GetLocalEntries(KEY_EMPTY, gotEntries), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotEntries)); + + GenerateAppointPrefixAndSizeRecords(entries2, entrySize, BATCH_RECORDS, {'k'}, {'w'}); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*delegate, entries2), OK); + + vector keys; + for (auto iter : entries2) { + keys.push_back(iter.key); + } + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*delegate, keys), OK); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option2.isMemoryDb)); +} +#endif +/** + * @tc.name: ComplexData 001 + * @tc.desc: Verify that it can continuously insert update and delete + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, ComplexData001, TestSize.Level1) +{ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_1_2); + entries2.push_back(ENTRY_2_3); + vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + + for (int index = 0; index < HUNDRED_TIMES; index++) { + /** + * @tc.steps: step1. call PutLocalBatch interface to insert entries1 to DB. + * @tc.expected: step1. PutLocalBatch succeed. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step2. call PutLocalBatch interface to update entries1 to entries2. + * @tc.expected: step2. update successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries2, gotValues)); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface to delete the records. + * @tc.expected: step3. delete successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), NOT_FOUND); + } +} + +/** + * @tc.name: ComplexData 002 + * @tc.desc: Verify that it can continuously insert update and delete + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, ComplexData002, TestSize.Level0) +{ + vector entries1, entries2, gotValues; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries2.push_back(ENTRY_1_3); + entries2.push_back(ENTRY_2_4); + entries2.push_back(ENTRY_3); + vector keys; + keys.push_back(KEY_2); + keys.push_back(KEY_3); + + /** + * @tc.steps: step1. call PutLocalBatch interface to insert entries1 to DB. + * @tc.expected: step1. PutLocalBatch succeed. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + + /** + * @tc.steps: step2. call PutLocalBatch interface to update entries1 to entries2. + * @tc.expected: step2. update successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries2, gotValues)); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface to delete the records. + * @tc.expected: step3. delete successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + entries2.pop_back(); + entries2.pop_back(); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries2, gotValues)); +} + +/** + * @tc.name: Exception 001 + * @tc.desc: Verify that local operate will calculate the total items in transantion. + * @tc.type: EXCEPTION + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Exception001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start the transantion. + * @tc.expected: step1. start success + */ + EXPECT_EQ(g_nbLocalBatchDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. call PutBatch interface to insert 128 records to sync DB, and call PutLocalBatch interface to + * insert (k1, v1) (k2, v2) to local DB. + * @tc.expected: step2. PutBatch succeed but PutLocalBatch will return OVER_MAX_LIMITS. + */ + vector entries1, entries2, gotValues; + EntrySize entrySize = {KEY_SIX_BYTE, ONE_K_LONG_STRING}; + GenerateAppointPrefixAndSizeRecords(entries1, entrySize, BATCH_RECORDS); + entries2.push_back(ENTRY_1); + entries2.push_back(ENTRY_2); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*g_nbLocalBatchDelegate, entries1), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OVER_MAX_LIMITS); + + /** + * @tc.steps: step3. call DeleteLocalBatch interface to Delete the 128 records from local DB. + * @tc.expected: step3. DeleteLocalBatch failed and returned OVER_MAX_LIMITS. + */ + vector keys1, keys2; + for (vector::iterator it = entries1.begin(); it != entries1.end(); it++) { + keys1.push_back(it->key); + } + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys1), OVER_MAX_LIMITS); + + /** + * @tc.steps: step4. call Delete interface to Delete the (k1, v1) from local DB. + * @tc.expected: step4. Delete failed and returned OVER_MAX_LIMITS. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->Delete(KEY_1), OVER_MAX_LIMITS); + + /** + * @tc.steps: step5. call DeleteLocalBatch interface to Delete (k1, v1)(k2, v2) from sync DB + * and then commit the transantion. + * @tc.expected: step5. Delete failed and returned OVER_MAX_LIMITS and the transantion commit success. + */ + keys2.push_back(KEY_1); + keys2.push_back(KEY_2); + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys2), OVER_MAX_LIMITS); + EXPECT_EQ(g_nbLocalBatchDelegate->Commit(), OK); + + /** + * @tc.steps: step6. call GetEntries and GetLocalEntries interface to check the data on sync and local DB. + * @tc.expected: step6. there are 128 records in sync DB but local DB is empty. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->GetEntries(KEY_EMPTY, gotValues), OK); + EXPECT_TRUE(CompareEntriesVector(entries1, gotValues)); + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotValues), NOT_FOUND); +} + +/** + * @tc.name: Observer 001 + * @tc.desc: Verify that different observer for different key will be triggered when PutLocalBatch. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer001, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + /** + * @tc.steps: step1. register observer of k1, k2, k3 of OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_LOCAL_ONLY, &observer2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_LOCAL_ONLY, &observer3), OK); + + /** + * @tc.steps: step2. PutLocalBatch (k1, v1), (k2, v2) to DB and check the 3 observers. + * @tc.expected: step2. PutLocalBatch and observer1, and observer2 was triggered but observer3 was not triggered. + */ + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + list changeList; + changeList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, changeList)); + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ZERO_TIME, INSERT_LIST, changeList)); + /** + * @tc.steps: step3. PutLocalBatch (k2, v3) (k3, v3) to insert (k3, v3) and update (k2, v3) and check the observers + * @tc.expected: step3. PutLocalBatch success and observer1 was not triggered, observer2 was triggered update mode, + * and observer3 was triggered by insert mode. + */ + observer1.Clear(); + observer2.Clear(); + entries2.push_back(ENTRY_2_3); + entries2.push_back(ENTRY_3); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OK); + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, INSERT_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, UPDATE_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ONE_TIME, INSERT_LIST, changeList)); +} + +/** + * @tc.name: Observer 002 + * @tc.desc: Verify that different observer for different key will be triggered when DeleteLocalBatch. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer002, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + vector entries, gotEntries; + entries.push_back(ENTRY_1); + entries.push_back(ENTRY_2); + entries.push_back(ENTRY_3); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries), OK); + /** + * @tc.steps: step1. register observer of k1, k2, k3 of OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_LOCAL_ONLY, &observer2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_LOCAL_ONLY, &observer3), OK); + + /** + * @tc.steps: step2. DeleteLocalBatch (k1, v1), (k2, v2) from DB and check the 3 observers. + * @tc.expected: step2. DeleteLocalBatch success and observer1, and observer2 was triggered by delete mode, + * but observer3 was not triggered. + */ + vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + list changeList; + changeList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, DELETE_LIST, changeList)); + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ZERO_TIME, DELETE_LIST, changeList)); + + /** + * @tc.steps: step3. use GetLocalEntries to check the data in DB. + * @tc.expected: step3. there is only (k3, v3) in DB. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->GetLocalEntries(KEY_EMPTY, gotEntries), OK); + entries.erase(entries.begin(), entries.begin() + 2); + EXPECT_TRUE(CompareEntriesVector(entries, gotEntries)); +} + +/** + * @tc.name: Observer 003 + * @tc.desc: Verify that PutLocalBatch operator one key many times, the observer can still be triggered but will be + * triggered one time. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer003, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + /** + * @tc.steps: step1. register observer of k1, k2, k3 of OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_LOCAL_ONLY, &observer2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_LOCAL_ONLY, &observer3), OK); + + /** + * @tc.steps: step2. PutLocalBatch (k1, v1), (k1, v2), (k2, v2) to DB and check the 3 observers. + * @tc.expected: step2. PutLocalBatch success and observer1 was triggered once time by insert mode, + * and observer2 was triggered by insert mode, but observer3 was not triggered. + */ + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_1_2); + entries1.push_back(ENTRY_2); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + + list changeList; + changeList.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, INSERT_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, INSERT_LIST, changeList)); + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ZERO_TIME, INSERT_LIST, changeList)); + + /** + * @tc.steps: step3. PutLocalBatch (k2, v3), (k2, v4), (k3, v3) to DB and check the 3 observers. + * @tc.expected: step3. PutLocalBatch success and observer1 was not triggered, and observer2 was triggered by + * update mode, and observer3 was triggered once by insert mode. + */ + observer1.Clear(); + observer2.Clear(); + entries2.push_back(ENTRY_2_3); + entries2.push_back(ENTRY_2_4); + entries2.push_back(ENTRY_3); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries2), OK); + + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ZERO_TIME, UPDATE_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2_4); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, UPDATE_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_3); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ONE_TIME, INSERT_LIST, changeList)); +} + +/** + * @tc.name: Observer 004 + * @tc.desc: Verify that DeleteLocalBatch operator one key many times, the observer can still be triggered but will be + * triggered one time. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer004, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3; + vector entries1, entries2; + entries1.push_back(ENTRY_1); + entries1.push_back(ENTRY_2); + entries1.push_back(ENTRY_3); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries1), OK); + /** + * @tc.steps: step1. register observer of k1, k2, k3 of OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_LOCAL_ONLY, &observer2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_3, OBSERVER_CHANGES_LOCAL_ONLY, &observer3), OK); + + /** + * @tc.steps: step2. DeleteLocalBatch (k1, k2, k1) from DB and check the 3 observers. + * @tc.expected: step2. DeleteLocalBatch success and observer1 and observer2 was triggered once time by delete mode, + * but observer3 was not triggered. + */ + vector keys; + keys.push_back(KEY_1); + keys.push_back(KEY_2); + keys.push_back(KEY_1); + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + list changeList; + changeList.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observer1, CHANGED_ONE_TIME, DELETE_LIST, changeList)); + changeList.clear(); + changeList.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observer2, CHANGED_ONE_TIME, DELETE_LIST, changeList)); + changeList.clear(); + EXPECT_TRUE(VerifyObserverResult(observer3, CHANGED_ZERO_TIME, DELETE_LIST, changeList)); +} + +/** + * @tc.name: Observer 005 + * @tc.desc: Verify that different observers of the same key will be triggered when the key is changed + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer005, TestSize.Level0) +{ + vector observer(OBSERVER_CNT_END); + /** + * @tc.steps: step1. register observer all the 8 observers of OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + for (unsigned long index = 0; index < OBSERVER_CNT_END; index++) { + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, + &observer[index]), OK); + } + + /** + * @tc.steps: step2. PutLocalBatch 10 data to DB and check the 8 observers. + * @tc.expected: step2. PutLocalBatch success and all the observers were triggered one insert callback and the + * callback list contains 10 data. + */ + vector entries; + vector keys; + GenerateFixedRecords(entries, keys, TEN_RECORDS, KEY_SIX_BYTE, ONE_K_LONG_STRING); + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries), OK); + list changeList; + for (auto it = entries.begin(); it != entries.end(); it++) { + changeList.push_back(*it); + } + for (auto it = observer.begin(); it != observer.end(); it++) { + EXPECT_TRUE(VerifyObserverResult(*it, CHANGED_ONE_TIME, INSERT_LIST, changeList)); + it->Clear(); + } + /** + * @tc.steps: step3. use PutLocalBatch to update the 10 data to DB and check the 8 observers. + * @tc.expected: step3. update success and all the observers were triggered one update callback and the + * callback list contains 10 data. + */ + for (auto it = entries.begin(); it != entries.end(); it++) { + it->value = {'v', 'v', 'v', 'v', 'v'}; + } + EXPECT_EQ(DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, entries), OK); + changeList.clear(); + for (auto it = entries.begin(); it != entries.end(); it++) { + it->value = {'v', 'v', 'v', 'v', 'v'}; + changeList.push_back(*it); + } + for (auto it = observer.begin(); it != observer.end(); it++) { + EXPECT_TRUE(VerifyObserverResult(*it, CHANGED_ONE_TIME, UPDATE_LIST, changeList)); + it->Clear(); + } + /** + * @tc.steps: step4. use DeleteLocalBatch to delete the 10 data from DB and check the 8 observers. + * @tc.expected: step4. delete success and all the observers were triggered one delete callback and the + * callback list contains 10 data. + */ + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, keys), OK); + for (auto it = observer.begin(); it != observer.end(); it++) { + EXPECT_TRUE(VerifyObserverResult(*it, CHANGED_ONE_TIME, DELETE_LIST, changeList)); + } +} + +/** + * @tc.name: Observer 006 + * @tc.desc: Operation data committed in a transaction will trigger batch callback in registered observer + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer006, TestSize.Level0) +{ + EXPECT_EQ(g_nbLocalBatchDelegate->Put(KEY_1, VALUE_1), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->Put(KEY_2, VALUE_2), OK); + vector insertEntries; + EntrySize entrySize = {KEY_SIX_BYTE, VALUE_ONE_HUNDRED_BYTE}; + GenerateAppointPrefixAndSizeRecords(insertEntries, entrySize, TEN_RECORDS, {'k'}, {'v', 'm'}); + DBStatus status = DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, insertEntries); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. register a local and a native observer separately, then start a transaction. + * @tc.expected: step1. operate successfully. + */ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerNative; + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, &observerNative), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->StartTransaction(), OK); + /** + * @tc.steps: step2. PutBatch 10 records, Put (k1,v1), Delete (k2). + * @tc.expected: step2. operate successfully. + */ + status = DistributedDBNbTestTools::PutBatch(*g_nbLocalBatchDelegate, insertEntries); + EXPECT_EQ(status, OK); + EXPECT_EQ(g_nbLocalBatchDelegate->Put(KEY_1, VALUE_2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->Delete(KEY_2), OK); + /** + * @tc.steps: step3. PutLocal (k1,v1), (k2,v2), DeleteLocal (k1,v1), + * use PutLocalBatch to update records from k1 to k5, use DeleteLocalBatch to delete keys from k6 to k10. + * @tc.expected: step3. operate successfully. + */ + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbLocalBatchDelegate, KEY_1, VALUE_1), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbLocalBatchDelegate, KEY_2, VALUE_2), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->DeleteLocal(KEY_1), OK); + vector localUpdateEntries; + GenerateAppointPrefixAndSizeRecords(localUpdateEntries, entrySize, FIVE_RECORDS, {'k'}, {'v', 'n'}); + status = DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, localUpdateEntries); + EXPECT_EQ(status, OK); + + vector localDeleteKeys; + vector localDeleteEntries; + for (size_t index = INDEX_FIFTH; index < insertEntries.size(); index++) { + localDeleteKeys.push_back(insertEntries[index].key); + localDeleteEntries.push_back(insertEntries[index]); + } + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbLocalBatchDelegate, localDeleteKeys), OK); + /** + * @tc.steps: step4. commit the transaction and verify corresponding data. + * @tc.expected: step4. delete success and all the observers were triggered one delete callback and the + * callback list contains 10 data. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->Commit(), OK); + EXPECT_TRUE(VerifyObserverResult(observerNative, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + vector updateEntries = { ENTRY_1_2 }; + EXPECT_TRUE(VerifyObserverResult(observerNative, CHANGED_ONE_TIME, UPDATE_LIST, updateEntries)); + vector deleteEntries = { ENTRY_2 }; + EXPECT_TRUE(VerifyObserverResult(observerNative, CHANGED_ONE_TIME, DELETE_LIST, deleteEntries)); + + vector localInsertEntries = { ENTRY_2 }; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, localInsertEntries)); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, localUpdateEntries)); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, localDeleteEntries)); + + EXPECT_EQ(g_nbLocalBatchDelegate->UnRegisterObserver(&observerLocal), OK); + EXPECT_EQ(g_nbLocalBatchDelegate->UnRegisterObserver(&observerNative), OK); +} + +/** + * @tc.name: Observer 007 + * @tc.desc: observer callback data only can be received after a transaction committed + * in multi-thread environment using the same delegate and observer. + * @tc.type: FUNC + * @tc.require: SR000EPA24 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbLocalBatchCrudTest, Observer007, TestSize.Level0) +{ + /** + * @tc.steps: step1. register a local observer. + * @tc.expected: step1. operate successfully. + */ + KvStoreObserverImpl observerLocal; + EXPECT_EQ(g_nbLocalBatchDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal), OK); + /** + * @tc.steps: step2. start a transaction, then PutBatch 10 records, PutLocal (k10,v10), + * and PutLocalBatch from k1 to k5. + * @tc.expected: step2. operate successfully. + */ + EXPECT_EQ(g_nbLocalBatchDelegate->StartTransaction(), OK); + EXPECT_EQ(DistributedDBNbTestTools::PutLocal(*g_nbLocalBatchDelegate, KEY_10, VALUE_10), OK); + vector insertEntries = { ENTRY_1, ENTRY_2, ENTRY_3, ENTRY_4, ENTRY_5 }; + DBStatus status = DistributedDBNbTestTools::PutLocalBatch(*g_nbLocalBatchDelegate, insertEntries); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step3. launch a thread to verify observer callback data, commit the transaction + * and then verify the data again. + * @tc.expected: step3. there is no data before committing the transaction + * and there are 6 records in insert list of the local observer after committing. + */ + insertEntries.push_back(ENTRY_10); + thread subThread([&]() { + vector emptyInsertEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, emptyInsertEntries)); + EXPECT_EQ(g_nbLocalBatchDelegate->Commit(), OK); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + }); + subThread.join(); + /** + * @tc.steps: step4. verify the data in main thread after the transaction committed in sub thread. + * @tc.expected: step4. there are 6 records in insert list of the local observer. + */ + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertEntries)); + EXPECT_EQ(g_nbLocalBatchDelegate->UnRegisterObserver(&observerLocal), OK); +} +} // end of namespace DistributeddbNbLocalBatchCrud \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_observer_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_observer_test.cpp new file mode 100755 index 000000000..0157ecf87 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_observer_test.cpp @@ -0,0 +1,2223 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#include "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "distributeddb_nb_test_tools.h" +#include "kv_store_delegate_manager.h" +#include "distributeddb_data_generator.h" +#include "distributed_test_tools.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbObserver { +const int CHANGED_ZERO_TIME = 0; +const int CHANGED_ONE_TIME = 1; +const int STORE_NUM = 2; + +const unsigned int ANY_RECORDS_NUM_START = 1; +const unsigned int ANY_RECORDS_NUM_END = 1000; + +const unsigned int LONG_TIME_TEST_SECONDS = 10; +const unsigned int LONG_TIME_INTERVAL_MILLSECONDS = 5; + +KvStoreNbDelegate *g_nbObserverDelegate = nullptr; +KvStoreDelegateManager *g_nbObserverManager = nullptr; + +struct ConcurParam { + unsigned int threadId_; + ReadOrWriteTag tag_; + Entry* entryPtr_; +}; + +DistributedDB::CipherPassword g_passwd1; +DistributedDB::CipherPassword g_passwd2; + +class DistributeddbNbObserverTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbObserverTest::SetUpTestCase(void) +{ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); + (void)g_passwd2.SetValue(PASSWD_VECTOR_2.data(), PASSWD_VECTOR_2.size()); +} + +void DistributeddbNbObserverTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbObserverTest::SetUp(void) +{ + MST_LOG("SetUpTest before case local."); + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, g_option); + EXPECT_TRUE(g_nbObserverManager != nullptr && g_nbObserverDelegate != nullptr); +} + +void DistributeddbNbObserverTest::TearDown(void) +{ + EXPECT_TRUE(EndCaseDeleteDB(g_nbObserverManager, g_nbObserverDelegate, STORE_ID_1, g_option.isMemoryDb)); + RemoveDir(NB_DIRECTOR); +} + +void RegisterAndUnRegisterObserver(ConcurParam* paramsPtr) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + DBStatus status = g_nbObserverDelegate->RegisterObserver( + paramsPtr->entryPtr_->key, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver( + paramsPtr->entryPtr_->key, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +void ConcurOperThread(ConcurParam* args) +{ + auto paramsPtr = static_cast(args); + DBStatus status; + Value valueResult; + + if (paramsPtr->tag_ == ReadOrWriteTag::READ) { + status = DistributedDBNbTestTools::Get(*g_nbObserverDelegate, paramsPtr->entryPtr_->key, valueResult); + if (valueResult.size() != 0) { + EXPECT_EQ(status, OK); + EXPECT_TRUE(DistributedDBNbTestTools::isValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } + } else if (paramsPtr->tag_ == ReadOrWriteTag::WRITE) { + status = DistributedDBNbTestTools::Put(*g_nbObserverDelegate, + paramsPtr->entryPtr_->key, paramsPtr->entryPtr_->value); + ASSERT_EQ(status, DBStatus::OK); + + status = DistributedDBNbTestTools::Get(*g_nbObserverDelegate, paramsPtr->entryPtr_->key, valueResult); + EXPECT_EQ(status, OK); + EXPECT_TRUE(DistributedDBNbTestTools::isValueEquals(valueResult, paramsPtr->entryPtr_->value)); + } else if (paramsPtr->tag_ == ReadOrWriteTag::DELETE) { + status = DistributedDBNbTestTools::Get(*g_nbObserverDelegate, paramsPtr->entryPtr_->key, valueResult); + if (valueResult.size() != 0) { + status = DistributedDBNbTestTools::Delete(*g_nbObserverDelegate, paramsPtr->entryPtr_->key); + ASSERT_EQ(status, DBStatus::OK); + + valueResult.clear(); + status = DistributedDBNbTestTools::Get(*g_nbObserverDelegate, paramsPtr->entryPtr_->key, valueResult); + EXPECT_EQ(status, NOT_FOUND); + EXPECT_EQ(valueResult.size(), (size_t)0); + } else { + status = DistributedDBNbTestTools::Delete(*g_nbObserverDelegate, paramsPtr->entryPtr_->key); + ASSERT_EQ(status, DBStatus::OK); + } + } else { + RegisterAndUnRegisterObserver(paramsPtr); + } +} + +/* + * @tc.name: RegisterData 001 + * @tc.desc: Verify that observer of local db and sync db can't effect each other. + * @tc.type: FUNC + * @tc.require: SR000CQDVH + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, RegisterData001, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + /** + * @tc.steps: step1. register local observer1 use OBSERVER_CHANGES_LOCAL_ONLY mode. + * @tc.expected: step1. register success. + */ + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. register sync observer2 use OBSERVER_CHANGES_NATIVE. + * @tc.expected: step2. register success. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step3. verify that if observer1 will be triggered when put (KEY_1, VALUE_1) to local db. + * @tc.expected: step3. observer1 will be response but observer2 won't. + */ + EXPECT_EQ((g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1)), OK); + vector insertLocalEntries; + insertLocalEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + /** + * @tc.steps: step4. verify that if observer1 will be triggered when delete (KEY_1, VALUE_1) from local db. + * @tc.expected: step4. observer1 will be response but observer2 won't. + */ + EXPECT_EQ((g_nbObserverDelegate->DeleteLocal(KEY_1)), OK); + vector deleteLocalEntries; + deleteLocalEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + /** + * @tc.steps: step5. verify that if observer2 will be triggered when put (KEY_1, VALUE_1) to sync db. + * @tc.expected: step5. observer2 will be response but observer1 won't. + */ + EXPECT_EQ((g_nbObserverDelegate->Put(KEY_1, VALUE_1)), OK); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + insertNativeEntries.clear(); + insertNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + /** + * @tc.steps: step6. verify that if observer2 will be triggered when delete (KEY_1, VALUE_1) from sync db. + * @tc.expected: step6. observer1 will be response but observer2 won't. + */ + EXPECT_EQ((g_nbObserverDelegate->Delete(KEY_1)), OK); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + deleteNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + g_nbObserverDelegate->UnRegisterObserver(&observerSync); +} + +void CheckObserverAllLocalValue(KvStoreObserverImpl &observerLocal) +{ + /** + * @tc.steps: step2. put one entries (KEY_1, VALUE_1) to local db. + * @tc.expected: step2. observerLocal will be response and the data observerLocal got is (KEY_1, VALUE_1). + */ + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + vector insertLocalEntries; + insertLocalEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertLocalEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. update one entries (KEY_2, VALUE_2) of local db. + * @tc.expected: step3. observerLocal will be response and the data observerLocal got is (KEY_2, VALUE_2). + */ + status = g_nbObserverDelegate->PutLocal(KEY_2, VALUE_2); + EXPECT_EQ(status, OK); + insertLocalEntries.clear(); + insertLocalEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertLocalEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step4. delete one entry of local db where key = KEY_1. + * @tc.expected: step4. observerLocal will be response and the data observerLocal got is (KEY_1, VALUE_1). + */ + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + vector deleteLocalEntries; + deleteLocalEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntries)); + observerLocal.Clear(); +} + +/* + * @tc.name: RegisterData 002 + * @tc.desc: Verify that can observer all records of local db. + * @tc.type: FUNC + * @tc.require: SR000CQDVH + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, RegisterData002, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + /** + * @tc.steps: step1. register local observerLocal use empty key KEY_EMPTY. + * @tc.expected: step1. register success. + */ + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + + CheckObserverAllLocalValue(observerLocal); + + /** + * @tc.steps: step5. put one entry (KEY_1, VALUE_1) to sync db. + * @tc.expected: step5. observerLocal won't be response. + */ + status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step6. put one entry (KEY_2, VALUE_2) to sync db. + * @tc.expected: step6. observerLocal won't be response. + */ + status = g_nbObserverDelegate->Put(KEY_2, VALUE_2); + EXPECT_EQ(status, OK); + insertNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step7. delete one entry from sync db where key = KEY_1. + * @tc.expected: step7. observerLocal won't be response. + */ + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + + g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + g_nbObserverDelegate->DeleteLocal(KEY_2); + g_nbObserverDelegate->Delete(KEY_2); +} + +void CheckObserverAllNativeValue(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync) +{ + /** + * @tc.steps: step2. put one entries (KEY_1, VALUE_1) to local db. + * @tc.expected: step2. observerLocal won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1), OK); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. update one entries (KEY_2, VALUE_2) of local db. + * @tc.expected: step3. observerLocal won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->PutLocal(KEY_2, VALUE_2), OK); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step4. delete one entry of local db where key = KEY_1. + * @tc.expected: step4. observerLocal won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->DeleteLocal(KEY_1), OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step5. put one entry (KEY_1, VALUE_1) to sync db. + * @tc.expected: step5. observerSync will be response and the data observerSync got is (KEY_1, VALUE_1). + */ + EXPECT_EQ(g_nbObserverDelegate->Put(KEY_1, VALUE_1), OK); + vector insertNativeEntries; + insertNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step6. update one entry (KEY_2, VALUE_3) to sync db. + * @tc.expected: step6. observerSync will be response and the data observerSync got is (KEY_2, VALUE_3). + */ + EXPECT_EQ(g_nbObserverDelegate->Put(KEY_2, VALUE_3), OK); + insertNativeEntries.clear(); + insertNativeEntries.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); +} + +/* + * @tc.name: RegisterData 003 + * @tc.desc: Verify that can observer all records of sync db. + * @tc.type: FUNC + * @tc.require: SR000CQDVH + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, RegisterData003, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + /** + * @tc.steps: step1. register sync observerSync use empty key KEY_EMPTY. + * @tc.expected: step1. register success. + */ + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + + CheckObserverAllNativeValue(observerLocal, observerSync); + + status = g_nbObserverDelegate->Put(KEY_2, VALUE_2); + vector updateNativeEntries; + updateNativeEntries.push_back(ENTRY_2); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, updateNativeEntries)); + observerSync.Clear(); + + EXPECT_EQ(g_nbObserverDelegate->Put(KEY_2, VALUE_3), OK); + updateNativeEntries.clear(); + updateNativeEntries.push_back(ENTRY_2_3); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, updateNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step7. delete one entry from sync db where key = KEY_1. + * @tc.expected: step7. observerSync will be response and the data observerSync got is (KEY_1, VALUE_1). + */ + EXPECT_EQ(g_nbObserverDelegate->Delete(KEY_1), OK); + vector deleteNativeEntries; + deleteNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + + g_nbObserverDelegate->UnRegisterObserver(&observerSync); + g_nbObserverDelegate->DeleteLocal(KEY_2); + g_nbObserverDelegate->Delete(KEY_2); +} + +/* + * @tc.name: UnRegister 001 + * @tc.desc: Verify that the record didn't register won't trigger the observer. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, UnRegister001, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + /** + * @tc.steps: step1. register local observerLocal use key = KEY_1. + * @tc.expected: step1. register success. + */ + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. register sync observerSync use key = KEY_2. + * @tc.expected: step2. register success. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_2, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step3. put one entries (KEY_2, VALUE_2) to local db. + * @tc.expected: step3. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->PutLocal(KEY_2, VALUE_2), OK); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step4. delete one entries from local db where key = KEY_2. + * @tc.expected: step4. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->DeleteLocal(KEY_2), OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step5. put one entries (KEY_1, VALUE_1) to sync db. + * @tc.expected: step5. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->Put(KEY_1, VALUE_1), OK); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + insertNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step6. delete one entries from sync db where key = KEY_1. + * @tc.expected: step6. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->Delete(KEY_1), OK); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + g_nbObserverDelegate->UnRegisterObserver(&observerSync); +} + +/* + * @tc.name: UnRegister 002 + * @tc.desc: Verify that UnRegister local of KEY_1 observer and sync observer won't be affected. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, UnRegister002, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. UnRegister local observer of KEY_1. + * @tc.expected: step1. UnRegister success. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. delete one entries from local db where key = KEY_1. + * @tc.expected: step2. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->DeleteLocal(KEY_1), OK); + vector deleteLocalEntries, deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. delete one entries from sync db where key = KEY_1. + * @tc.expected: step3. observerLocal won't be response but observerSync will be response + * and got record (KEY_1, VALUE_1). + */ + EXPECT_EQ(g_nbObserverDelegate->Delete(KEY_1), OK); + deleteNativeEntries.clear(); + deleteNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + observerSync.Clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerSync); +} + +/* + * @tc.name: UnRegister 003 + * @tc.desc: Verify that UnRegister sync observer of KEY_1 and local observer won't be affected. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, UnRegister003, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_TRUE(status == OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step1. UnRegister sync observer of KEY_1. + * @tc.expected: step1. UnRegister success. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_TRUE(status == OK); + /** + * @tc.steps: step2. delete one entries from local db where key = KEY_1. + * @tc.expected: step2. observerLocal will be response and got record (KEY_1, VALUE_1) + * but observerSync won't be response. + */ + EXPECT_TRUE(g_nbObserverDelegate->DeleteLocal(KEY_1) == OK); + vector deleteLocalEntry; + deleteLocalEntry.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntry)); + vector deleteNativeEntry; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntry)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. delete one entries from sync db where key = KEY_1. + * @tc.expected: step3. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_TRUE(g_nbObserverDelegate->Delete(KEY_1) == OK); + deleteLocalEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntry)); + deleteNativeEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntry)); + observerSync.Clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerLocal); +} + +/* + * @tc.name: UnRegister 004 + * @tc.desc: Verify that UnRegister local observer of all-key and sync observer won't be affected. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, UnRegister004, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. UnRegister local observer of KEY_EMPTY. + * @tc.expected: step1. UnRegister success. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. delete one entries from local db where key = KEY_1. + * @tc.expected: step2. observerLocal won't be response but observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->DeleteLocal(KEY_1), OK); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. delete one entries from sync db where key = KEY_1. + * @tc.expected: step3. observerLocal won't be response but observerSync will be response + * and got record (KEY_1, VALUE_1). + */ + EXPECT_EQ(g_nbObserverDelegate->Delete(KEY_1), OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + deleteNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerSync); +} + +/* + * @tc.name: UnRegister 005 + * @tc.desc: Verify that UnRegister sync observer of all-key and local observer won't be affected. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, UnRegister005, TestSize.Level0) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. UnRegister sync observer of KEY_EMPTY. + * @tc.expected: step1. UnRegister success. + */ + EXPECT_EQ(g_nbObserverDelegate->UnRegisterObserver(&observerSync), OK); + /** + * @tc.steps: step2. delete one entries from local db where key = KEY_1. + * @tc.expected: step2. observerLocal will be response and got record (KEY_1, VALUE_1) + * but observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->DeleteLocal(KEY_1), OK); + vector deleteLocalEntries; + deleteLocalEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step3. delete one entries from sync db where key = KEY_1. + * @tc.expected: step3. observerLocal won't be response and observerSync won't be response. + */ + EXPECT_EQ(g_nbObserverDelegate->Delete(KEY_1), OK); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + g_nbObserverDelegate->UnRegisterObserver(&observerLocal); +} + +/* + * @tc.name: ParamCheck 001 + * @tc.desc: Verify that can check effectiveness of params mode. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, ParamCheck001, TestSize.Level0) +{ + KvStoreObserverImpl observer1, observer2, observer3, observer4, observer; + + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_NATIVE, &observer2); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_FOREIGN, &observer3); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, + OBSERVER_CHANGES_FOREIGN | OBSERVER_CHANGES_NATIVE, &observer4); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. Register observer with the mode is not in (1, 2, 3, 4). + * @tc.expected: step1. Register failed and return INVALID_ARGS. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_1, 0, &observer); // invalid mode number 0 + EXPECT_EQ(status, INVALID_ARGS); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, 5, &observer); // invalid mode number 5 + EXPECT_EQ(status, INVALID_ARGS); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, -1, &observer); // invalid mode number -1 + EXPECT_EQ(status, INVALID_ARGS); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, 2147483647, &observer); // invalid mode number 2147483647 + EXPECT_EQ(status, INVALID_ARGS); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, -2147483648, &observer); // invalid mode number -2147483648 + EXPECT_EQ(status, INVALID_ARGS); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, 999, &observer); // invalid mode number 999 + EXPECT_EQ(status, INVALID_ARGS); + g_nbObserverDelegate->UnRegisterObserver(&observer1); + g_nbObserverDelegate->UnRegisterObserver(&observer2); + g_nbObserverDelegate->UnRegisterObserver(&observer3); + g_nbObserverDelegate->UnRegisterObserver(&observer4); +} + +/* + * @tc.name: ParamCheck 002 + * @tc.desc: Verify that can check effectiveness of params key. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, ParamCheck002, TestSize.Level0) +{ + DistributedDB::Key eKey1, eKey2, eKey3; + eKey1.assign(ONE_K_LONG_STRING, (uint8_t)'a'); + eKey2.assign(ONE_K_LONG_STRING + 1, (uint8_t)'b'); + eKey3 = { 'a', 'b', 'c', 'D', 'E', 'F', '2', '4', '5', 199, 1, 255, 0 }; + KvStoreObserverImpl observer1, observer2, observer3, observer4, observer5, observer6; + + /** + * @tc.steps: step1. Register local observer with the key = eKey1 size of which is 1024. + * @tc.expected: step1. Register success. + */ + DBStatus status = + g_nbObserverDelegate->RegisterObserver(eKey1, OBSERVER_CHANGES_LOCAL_ONLY, &observer1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. Register local observer with the key = eKey2 size of which is 1025. + * @tc.expected: step2. Register failed and return INVALID_ARGS. + */ + status = g_nbObserverDelegate->RegisterObserver(eKey2, OBSERVER_CHANGES_LOCAL_ONLY, &observer2); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step3. Register local observer with the key = eKey3 which contains + * [a-zA-Z0-9], [\0-\255], chinese and latins. + * @tc.expected: step3. Register failed and return INVALID_ARGS. + */ + status = g_nbObserverDelegate->RegisterObserver(eKey3, OBSERVER_CHANGES_LOCAL_ONLY, &observer3); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step4. Register sync observer with the key = eKey1 size of which is 1024. + * @tc.expected: step4. Register success. + */ + status = g_nbObserverDelegate->RegisterObserver(eKey1, OBSERVER_CHANGES_NATIVE, &observer4); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step5. Register local observer with the key = eKey2 size of which is 1025. + * @tc.expected: step5. Register failed and return INVALID_ARGS. + */ + status = g_nbObserverDelegate->RegisterObserver(eKey2, OBSERVER_CHANGES_NATIVE, &observer5); + EXPECT_EQ(status, INVALID_ARGS); + /** + * @tc.steps: step6. Register local observer with the key = eKey3 which contains + * [a-zA-Z0-9], [\0-\255], chinese and latins. + * @tc.expected: step6. Register failed and return INVALID_ARGS. + */ + status = g_nbObserverDelegate->RegisterObserver(eKey3, OBSERVER_CHANGES_NATIVE, &observer6); + EXPECT_EQ(status, OK); + g_nbObserverDelegate->UnRegisterObserver(&observer1); + g_nbObserverDelegate->UnRegisterObserver(&observer2); + g_nbObserverDelegate->UnRegisterObserver(&observer3); + g_nbObserverDelegate->UnRegisterObserver(&observer4); + g_nbObserverDelegate->UnRegisterObserver(&observer5); + g_nbObserverDelegate->UnRegisterObserver(&observer6); +} + +void CheckPressureActionInNative(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync) +{ + /** + * @tc.steps: step3. put one entries (KEY_1, VALUE_1) to local db. + * @tc.expected: step3. observerLocal won't be response. + */ + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step4. put one entries (KEY_1, VALUE_1) to sync db. + * @tc.expected: step4. observerSync will be response and the data observerSync got is (KEY_1, VALUE_1). + */ + status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + insertNativeEntries.clear(); + insertNativeEntries.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + /** + * @tc.steps: step5. update one entries (KEY_1, VALUE_2) to local db. + * @tc.expected: step5. observerLocal won't be response. + */ + status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + vector updateLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + vector updateNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step6. update one entries (KEY_1, VALUE_2) to sync db. + * @tc.expected: step6. observerSync will be response and the data observerSync got is (KEY_1, VALUE_2). + */ + status = g_nbObserverDelegate->Put(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + updateLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + updateNativeEntries.clear(); + updateNativeEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, updateNativeEntries)); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 001 + * @tc.desc: Verify that local db can observer the key that do not exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure001, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + /** + * @tc.steps: step1. Register local observer with the key = KEY_A_1 which do not exist in db. + * @tc.expected: step1. Register success. + */ + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_A_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. Register sync observer with the key = KEY_EMPTY. + * @tc.expected: step2. Register success. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + CheckPressureActionInNative(observerLocal, observerSync); + + /** + * @tc.steps: step7. delete one entries from local db where key = KEY_1. + * @tc.expected: step7. observerLocal won't be response. + */ + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step8. delete one entries from sync db where key = KEY_1. + * @tc.expected: step8. observerLocal won't be response but observerSync will be response and + * the data observerSync got is (KEY_1, VALUE_2). + */ + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + deleteNativeEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +void CheckPressureActionInLocal(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync) +{ + /** + * @tc.steps: step3. put one entries (KEY_1, VALUE_1) to local db. + * @tc.expected: step3. observerLocal will be response and the data observerLocal got is (KEY_1, VALUE_1). + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + vector insertLocalEntry; + insertLocalEntry.push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, insertLocalEntry)); + vector insertNativeEntry; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntry)); + observerLocal.Clear(); + + /** + * @tc.steps: step4. put one entries (KEY_1, VALUE_1) to sync db. + * @tc.expected: step4. observerLocal won't be response. + */ + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + insertLocalEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntry)); + insertNativeEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntry)); + observerSync.Clear(); + + /** + * @tc.steps: step5. update one entries (KEY_1, VALUE_2) to local db. + * @tc.expected: step5. observerLocal will be response and the data observerLocal got is (KEY_1, VALUE_2). + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + vector updateLocalEntry; + updateLocalEntry.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, updateLocalEntry)); + vector updateNativeEntry; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntry)); + observerLocal.Clear(); + + /** + * @tc.steps: step6. update one entries (KEY_1, VALUE_2) to sync db. + * @tc.expected: step6. observerSync won't be response. + */ + g_nbObserverDelegate->Put(KEY_1, VALUE_2); + updateLocalEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntry)); + updateNativeEntry.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntry)); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 002 + * @tc.desc: Verify that sync db can observer the key that do not exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure002, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + /** + * @tc.steps: step1. Register local observer with the key = KEY_EMPTY. + * @tc.expected: step1. Register success. + */ + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step2. Register sync observer with the key = KEY_A_1 which do not exist in db. + * @tc.expected: step2. Register success. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_A_1, OBSERVER_CHANGES_NATIVE, &observerSync); + EXPECT_EQ(status, OK); + + CheckPressureActionInLocal(observerLocal, observerSync); + + /** + * @tc.steps: step7. delete one entries from local db where key = KEY_1. + * @tc.expected: step7. observerLocal will be response and the data observerLocal got is (KEY_1, VALUE_2) + * but observerSync won't be response. + */ + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + vector deleteLocalEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_2 }; + deleteLocalEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step8. delete one entries from sync db where key = KEY_1. + * @tc.expected: step8. observerSync won't be response. + */ + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + deleteNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + observerSync.Clear(); + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +/* + * @tc.name: Pressure 003 + * @tc.desc: Verify that can't unregister observer that do not exist. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure003, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + /** + * @tc.steps: step1. UnRegister local and sync Observer. + * @tc.expected: step1. it will be both failed to unregister observerLocal and observerSync. + */ + DBStatus status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, NOT_FOUND); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, NOT_FOUND); +} + +/* + * @tc.name: Pressure 004 + * @tc.desc: Verify that can't unregister nullptr. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure004, TestSize.Level1) +{ + /** + * @tc.steps: step1. UnRegister nullpter. + * @tc.expected: step1. it will be failed to unregister nullptr and return INVALID_ARGS. + */ + DBStatus status = g_nbObserverDelegate->UnRegisterObserver(nullptr); + + EXPECT_EQ(status, INVALID_ARGS); +} + +void CheckPressureActionAfterUnregister(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync) +{ + /** + * @tc.steps: step4. Crud to local and sync db. + * @tc.expected: step4. Both observerLocal and observerSync won't be response. + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + insertNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + vector updateNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntries)); + vector updateLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_2); + updateNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntries)); + updateLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + observerSync.Clear(); + + DBStatus status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); +} + +/* + * @tc.name: Pressure 005 + * @tc.desc: Verify that can't unregister observer repeately. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure005, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + DBStatus status = + g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. UnRegister local and sync observer the first time. + * @tc.expected: step1. it will be both success to unregister observerLocal and observerSync. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. UnRegister local and sync observer the second time. + * @tc.expected: step2. both failed to unregister observerLocal and observerSync, and return NOT_FOUND. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, NOT_FOUND); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, NOT_FOUND); + + /** + * @tc.steps: step3. UnRegister local and sync observer the third time. + * @tc.expected: step3. both failed to unregister observerLocal and observerSync, and return NOT_FOUND. + */ + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, NOT_FOUND); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, NOT_FOUND); + + CheckPressureActionAfterUnregister(observerLocal, observerSync); + + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 006 + * @tc.desc: Verify that can register and unregister observer repeately. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure006, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. Register and UnRegister the observer repeately. + * @tc.expected: step1. Register and unregister observerLocal and observerSync success each time. + */ + for (unsigned int opCnt = NB_OPERATION_CNT_START; opCnt < NB_OPERATION_CNT_END; ++opCnt) { + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_1, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); + } + + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); +} + +void CheckPressureForRepeatAction(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync) +{ + /** + * @tc.steps: step2. crud KEY_1 of local and sync db. + * @tc.expected: step2. observerLocal and observerSync can response each time. + */ + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + vector updateLocalEntries; + updateLocalEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, updateLocalEntries)); + vector updateNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntries)); + observerLocal.Clear(); + + status = g_nbObserverDelegate->Put(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + updateLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + updateNativeEntries.clear(); + updateNativeEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, updateNativeEntries)); + observerSync.Clear(); + + vector deleteLocalEntries; + vector deleteNativeEntries; + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + deleteLocalEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, deleteLocalEntries)); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + + deleteNativeEntries.push_back(ENTRY_1_2); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, deleteNativeEntries)); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 007 + * @tc.desc: Verify that can't register an observer of the same key repeately without unrigister. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure007, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal, observerSync; + DBStatus status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. Register a local and a sync observer of one same key KEY_EMPTY 5 times separately. + * @tc.expected: step1. Register observerLocal and observerSync success first time and failed later. + */ + for (unsigned int opCnt = NB_OPERATION_CNT_START; opCnt < NB_OPERATION_CNT_END; ++opCnt) { + status = g_nbObserverDelegate->RegisterObserver(KEY_1, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + if (opCnt == NB_OPERATION_CNT_START) { + EXPECT_EQ(status, OK); + } else { + EXPECT_EQ(status, DB_ERROR); + } + status = g_nbObserverDelegate->RegisterObserver(KEY_1, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + if (opCnt == NB_OPERATION_CNT_START) { + EXPECT_EQ(status, OK); + } else { + EXPECT_EQ(status, DB_ERROR); + } + } + + CheckPressureForRepeatAction(observerLocal, observerSync); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +void CheckPressureForLocalRepeat(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync, + vector< vector > &entries) +{ + /** + * @tc.steps: step2. Crud on local db. + * @tc.expected: step2. Each operator observerLocal response one time. + */ + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + entries[INSERT_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerLocal.Clear(); + + status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + + entry.value = VALUE_2; + entries[UPDATE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerLocal.Clear(); + + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + entries[DELETE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerLocal.Clear(); + + /** + * @tc.steps: step3. Crud on sync db. + * @tc.expected: step3. Each operator observerSync won't be response. + */ + status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + entries[INSERT_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 008 + * @tc.desc: Verify that can't register a local observer of all key repeately. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure008, TestSize.Level1) +{ + DBStatus status; + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + /** + * @tc.steps: step1. Register a local observer of key KEY_EMPTY 5 times repeately. + * @tc.expected: step1. Register observerLocal success first time and failed later. + */ + for (unsigned int opCnt = NB_OPERATION_CNT_START; opCnt < NB_OPERATION_CNT_END; ++opCnt) { + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + if (opCnt == NB_OPERATION_CNT_START) { + EXPECT_EQ(status, OK); + } else { + EXPECT_EQ(status, DB_ERROR); + } + } + + vector< vector > entries(6); // 6 element + CheckPressureForLocalRepeat(observerLocal, observerSync, entries); + + status = g_nbObserverDelegate->Put(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + entries[UPDATE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerSync.Clear(); + + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + entries[DELETE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); +} + +void CheckPressureForNativeRepeat(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync, + vector< vector > &entries) +{ + /** + * @tc.steps: step2. Crud on local db. + * @tc.expected: step2. Each operator observerLocal won't be response. + */ + DBStatus status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerLocal.Clear(); + + status = g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerLocal.Clear(); + + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerLocal.Clear(); + + /** + * @tc.steps: step3. Crud on Sync db. + * @tc.expected: step3. Each operator observerSync will be response one time. + */ + status = g_nbObserverDelegate->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + entries[INSERT_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + entries[INSERT_NATIVE].push_back(ENTRY_1); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerSync.Clear(); +} + +/* + * @tc.name: Pressure 009 + * @tc.desc: Verify that register a sync observer of the same key repeately. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure009, TestSize.Level1) +{ + DBStatus status; + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + /** + * @tc.steps: step1. Register a sync observer of key KEY_EMPTY 5 times repeately. + * @tc.expected: step1. Register observerSync success first time and failed later. + */ + for (unsigned int opCnt = NB_OPERATION_CNT_START; opCnt < NB_OPERATION_CNT_END; ++opCnt) { + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + if (opCnt == NB_OPERATION_CNT_START) { + EXPECT_EQ(status, OK); + } else { + EXPECT_EQ(status, DB_ERROR); + } + } + + vector< vector > entries(6); // 6 element + CheckPressureForNativeRepeat(observerLocal, observerSync, entries); + + status = g_nbObserverDelegate->Put(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + entries[UPDATE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_NATIVE].clear(); + DistributedDB::Entry entry = { KEY_1, VALUE_2 }; + entries[UPDATE_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerSync.Clear(); + + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); + entries[DELETE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + entries[DELETE_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +/* + * @tc.name: Pressure 012 + * @tc.desc: Verify that insert unusual key-value can't trigger the observer. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure012, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. put a unusual key = KEY_EMPTY to local and sync db and check corresponding observer. + * @tc.expected: step1. both observerLocal and observerSync won't be response. + */ + g_nbObserverDelegate->PutLocal(KEY_EMPTY, VALUE_1); + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_EMPTY, VALUE_1); + insertLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + insertNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +/* + * @tc.name: Pressure 013 + * @tc.desc: Verify that delete the record that do not exist can't trigger the observer. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure013, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. delete a record that do not exist in the local and sync db and check corresponding observer. + * @tc.expected: step1. both observerLocal and observerSync won't be response. + */ + g_nbObserverDelegate->DeleteLocal(KEY_1); + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerLocal.Clear(); + + g_nbObserverDelegate->Delete(KEY_1); + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +/* + * @tc.name: Pressure 014 + * @tc.desc: Verify that delete the record that do not exist can't trigger the observer. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure014, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. put a record (KEY_1, VALUE_1) to local db and check observerLocal. + * @tc.expected: step1. observerLocal will be response and observerSync won't be response. + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + vector updateLocalEntries; + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + updateLocalEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, updateLocalEntries)); + vector updateNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, updateNativeEntries)); + observerLocal.Clear(); + + /** + * @tc.steps: step2. put a record (KEY_1, VALUE_1) to sync db and check observerSync. + * @tc.expected: step2. observerLocal won't be response and observerSync will be response. + */ + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + updateLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, updateLocalEntries)); + updateNativeEntries.clear(); + updateNativeEntries.push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, updateNativeEntries)); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->Delete(KEY_1); + EXPECT_EQ(status, OK); +} + +void CheckPressureAfterClose(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync, + vector< vector > &entries) +{ + /** + * @tc.steps: step3. Crud on local and sync db and check corresponding observerLocal or observerSync. + * @tc.expected: step3. nor observerLocal neither observerSync will be response. + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + entries[INSERT_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerSync.Clear(); + + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_2); + entries[UPDATE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerSync.Clear(); + + DBStatus status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerLocal.Clear(); + + status = g_nbObserverDelegate->Delete(KEY_1); + entries[DELETE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerSync.Clear(); + + g_nbObserverDelegate->DeleteLocal(KEY_1); + g_nbObserverDelegate->Delete(KEY_1); +} +/* + * @tc.name: Pressure 015 + * @tc.desc: Verify that close db and open it again, observer won't be triggered again. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure015, TestSize.Level1) +{ + KvStoreObserverImpl observerLocal, observerSync; + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step1. close db. + * @tc.expected: step1. closed success. + */ + EXPECT_EQ(g_nbObserverManager->CloseKvStore(g_nbObserverDelegate), OK); + g_nbObserverDelegate = nullptr; + delete g_nbObserverManager; + g_nbObserverManager = nullptr; + /** + * @tc.steps: step2. open db again. + * @tc.expected: step2. opened success. + */ + Option option = g_option; + if (!option.isMemoryDb) { + option.createIfNecessary = IS_NOT_NEED_CREATE; + } + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, option); + ASSERT_TRUE(g_nbObserverManager != nullptr && g_nbObserverDelegate != nullptr); + + vector< vector > entries(6); // 6 element + CheckPressureAfterClose(observerLocal, observerSync, entries); + + vector insertLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, insertLocalEntries)); + vector insertNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, insertNativeEntries)); +} + +void CheckPressureAfterReopen(KvStoreObserverImpl &observerLocal, KvStoreObserverImpl &observerSync, + vector< vector > &entries) +{ + /** + * @tc.steps: step5. Crud on local and sync db and check corresponding observer. + * @tc.expected: step5. It will be correctly triggered when crud where key = KEY_1. + */ + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_1); + entries[INSERT_LOCAL].clear(); + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + entries[INSERT_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_1); + entries[INSERT_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + entries[INSERT_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerSync.Clear(); + g_nbObserverDelegate->PutLocal(KEY_1, VALUE_2); + entries[UPDATE_LOCAL].clear(); + entry.value = VALUE_2; + entries[UPDATE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerLocal.Clear(); + + g_nbObserverDelegate->Put(KEY_1, VALUE_2); + entries[UPDATE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_NATIVE].clear(); + entries[UPDATE_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerSync.Clear(); + + DBStatus status = g_nbObserverDelegate->DeleteLocal(KEY_1); + EXPECT_EQ(status, OK); + entries[DELETE_LOCAL].clear(); + entries[DELETE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerLocal.Clear(); +} + +/* + * @tc.name: Pressure 016 + * @tc.desc: Verify that close db and open it again, observer won't be triggered again, + * register observer again and it will response. + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure016, TestSize.Level2) +{ + KvStoreObserverImpl observerSync, observerLocal; + DBStatus status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + /** + * @tc.steps: step1. close db. + * @tc.expected: step1. closed success. + */ + EXPECT_EQ(g_nbObserverManager->CloseKvStore(g_nbObserverDelegate), OK); + g_nbObserverDelegate = nullptr; + delete g_nbObserverManager; + g_nbObserverManager = nullptr; + /** + * @tc.steps: step2. open db again. + * @tc.expected: step2. opened success. + */ + if (g_option.isMemoryDb) { + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, g_option); + } else { + Option option = g_option; + option.createIfNecessary = IS_NOT_NEED_CREATE; + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, option); + } + ASSERT_TRUE(g_nbObserverManager != nullptr && g_nbObserverDelegate != nullptr); + vector< vector > entries(6); // 6 element + CheckPressureAfterClose(observerLocal, observerSync, entries); + + /** + * @tc.steps: step3. register observerLocal and observerSync on key = KEY_EMPTY again. + * @tc.expected: step3. register success. + */ + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + CheckPressureAfterReopen(observerLocal, observerSync, entries); + + status = g_nbObserverDelegate->Delete(KEY_1); + entries[DELETE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + DistributedDB::Entry entry = { KEY_1, VALUE_2 }; + entries[DELETE_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerSync.Clear(); + + status = g_nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = g_nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); +} + +/* + * @tc.name: Pressure 017 + * @tc.desc: Verify that close db and observer won't be triggered again, + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure017, TestSize.Level2) +{ + KvStoreObserverImpl observerSync, observerLocal; + DBStatus registerStatus = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(registerStatus, OK); + registerStatus = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(registerStatus, OK); + + /** + * @tc.steps: step1. close db and check whether the observer was triggered. + * @tc.expected: step1. closed success and the observer won't response. + */ + EXPECT_EQ(g_nbObserverManager->CloseKvStore(g_nbObserverDelegate), OK); + g_nbObserverDelegate = nullptr; + delete g_nbObserverManager; + g_nbObserverManager = nullptr; + + /** + * @tc.steps: step2. check the local and sync observer after the data base was closed. + * @tc.expected: step2. neither the local observer nor the sync observer was triggered. + */ + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + + if (g_option.isMemoryDb) { + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, g_option); + } else { + Option option = g_option; + option.createIfNecessary = IS_NOT_NEED_CREATE; + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, option); + } + ASSERT_TRUE(g_nbObserverManager != nullptr && g_nbObserverDelegate != nullptr); +} + +/* + * @tc.name: Pressure 018 + * @tc.desc: Verify that close db and observer won't be triggered again, + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure018, TestSize.Level2) +{ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + DBStatus statusLocal = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(statusLocal, OK); + DBStatus statusSync = g_nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(statusSync, OK); + + /** + * @tc.steps: step1. close and delete db and check whether the observer was triggered. + * @tc.expected: step1. closed and deleted success and the observer won't response. + */ + EXPECT_EQ(g_nbObserverManager->CloseKvStore(g_nbObserverDelegate), OK); + g_nbObserverDelegate = nullptr; + vector deleteLocalEntries; + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + vector deleteNativeEntries; + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + + if (!g_option.isMemoryDb) { + DBStatus status = g_nbObserverManager->DeleteKvStore(STORE_ID_1); + EXPECT_TRUE(status == OK); + } + delete g_nbObserverManager; + g_nbObserverManager = nullptr; + deleteLocalEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocal, CHANGED_ZERO_TIME, DELETE_LIST, deleteLocalEntries)); + deleteNativeEntries.clear(); + EXPECT_TRUE(VerifyObserverResult(observerSync, CHANGED_ZERO_TIME, DELETE_LIST, deleteNativeEntries)); + + RemoveDir(DIRECTOR); + g_nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_nbObserverManager, + g_dbParameter1, g_option); + ASSERT_TRUE(g_nbObserverManager != nullptr && g_nbObserverDelegate != nullptr); +} + +void CheckPressureAcrossDatabase(vector &nbDelegateVec, KvStoreObserverImpl *observerLocals, + KvStoreObserverImpl *observerSyncs, vector< vector > &entries, unsigned int &opCnt) +{ + DBStatus status = nbDelegateVec[opCnt]->PutLocal(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + DistributedDB::Entry entry = { KEY_1, VALUE_1 }; + entries[INSERT_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + observerLocals[opCnt].Clear(); + status = nbDelegateVec[opCnt]->Put(KEY_1, VALUE_1); + EXPECT_EQ(status, OK); + entries[INSERT_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ZERO_TIME, INSERT_LIST, entries[INSERT_LOCAL])); + entries[INSERT_NATIVE].clear(); + entries[INSERT_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ONE_TIME, INSERT_LIST, entries[INSERT_NATIVE])); + entries[INSERT_NATIVE].clear(); + observerSyncs[opCnt].Clear(); + status = nbDelegateVec[opCnt]->PutLocal(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + entry.value = VALUE_2; + entries[UPDATE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_NATIVE])); + observerLocals[opCnt].Clear(); + status = nbDelegateVec[opCnt]->Put(KEY_1, VALUE_2); + EXPECT_EQ(status, OK); + entries[UPDATE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ZERO_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_LOCAL].clear(); + entries[UPDATE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ONE_TIME, UPDATE_LIST, entries[UPDATE_LOCAL])); + entries[UPDATE_LOCAL].clear(); + observerSyncs[opCnt].Clear(); + EXPECT_EQ((nbDelegateVec[opCnt]->DeleteLocal(KEY_1)), OK); + entries[DELETE_LOCAL].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + observerLocals[opCnt].Clear(); + EXPECT_EQ((nbDelegateVec[opCnt]->Delete(KEY_1)), OK); + entries[DELETE_LOCAL].clear(); + EXPECT_TRUE(VerifyObserverResult(observerLocals[opCnt], CHANGED_ZERO_TIME, DELETE_LIST, entries[DELETE_LOCAL])); + entries[DELETE_NATIVE].clear(); + entries[DELETE_NATIVE].push_back(entry); + EXPECT_TRUE(VerifyObserverResult(observerSyncs[opCnt], CHANGED_ONE_TIME, DELETE_LIST, entries[DELETE_NATIVE])); + entries[DELETE_NATIVE].clear(); + observerSyncs[opCnt].Clear(); +} + +/* + * @tc.name: Pressure 019 + * @tc.desc: Verify that close db and observer won't be triggered again, + * @tc.type: FUNC + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure019, TestSize.Level2) +{ + KvStoreDelegateManager *nbObserverManager = nullptr; + vector storeIds = { STORE_ID_2, STORE_ID_3 }; + vector nbDelegateVec; + DBStatus status = DistributedDBNbTestTools::GetNbDelegateStoresSuccess(nbObserverManager, + nbDelegateVec, storeIds, APP_ID_2, USER_ID_2, g_option); + EXPECT_EQ(status, OK); + MST_LOG("nbDelegateVec.size() = %zu", nbDelegateVec.size()); + + /** + * @tc.steps: step1. register different observer on different stores. + * @tc.expected: step1. each observer is registered successfully. + */ + KvStoreObserverImpl observerLocals[STORE_NUM]; + KvStoreObserverImpl observerSyncs[STORE_NUM]; + for (unsigned int opCnt = 0; opCnt < static_cast(nbDelegateVec.size()); ++opCnt) { + MST_LOG("opCnt = %d", opCnt); + status = nbDelegateVec[opCnt]->RegisterObserver(KEY_EMPTY, OBSERVER_CHANGES_LOCAL_ONLY, &observerLocals[opCnt]); + EXPECT_EQ(status, OK); + status = nbDelegateVec[opCnt]->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSyncs[opCnt]); + EXPECT_EQ(status, OK); + } + + /** + * @tc.steps: step2. crud on each db of corresponding observer on different stores. + * @tc.expected: step2. each observer is triggered successfully. + */ + vector< vector > entries(6); // 6 element + for (unsigned int opCnt = 0; opCnt < static_cast(nbDelegateVec.size()); ++opCnt) { + CheckPressureAcrossDatabase(nbDelegateVec, observerLocals, observerSyncs, entries, opCnt); + } + for (unsigned int opCnt = 0; opCnt < static_cast(nbDelegateVec.size()); ++opCnt) { + status = nbDelegateVec[opCnt]->UnRegisterObserver(&observerLocals[opCnt]); + EXPECT_EQ(status, OK); + status = nbDelegateVec[opCnt]->UnRegisterObserver(&observerSyncs[opCnt]); + EXPECT_EQ(status, OK); + } + + unsigned int delegateVecSize = static_cast(nbDelegateVec.size()); + for (unsigned int opCnt = 0; opCnt < delegateVecSize; ++opCnt) { + EXPECT_EQ(nbObserverManager->CloseKvStore(nbDelegateVec[opCnt]), OK); + nbDelegateVec[opCnt] = nullptr; + } + delete nbObserverManager; + nbObserverManager = nullptr; +} + +void CheckPressureLongCompare(vector &entriesBatch, ConcurParam *threadParam, Entry &entryCurrent) +{ + bool isExist = false; + if (threadParam->tag_ == ReadOrWriteTag::WRITE) { + for (auto &entry : entriesBatch) { + if (CompareVector(entry.key, entryCurrent.key)) { + isExist = true; + } + } + if (!isExist) { + entriesBatch.push_back(entryCurrent); + } + } else if (threadParam->tag_ == ReadOrWriteTag::DELETE) { + for (unsigned int index = 0; index < static_cast(entriesBatch.size()); ++index) { + if (CompareVector(entriesBatch[index].key, entryCurrent.key)) { + entriesBatch.erase(entriesBatch.begin() + index); + } + } + } +} + +void CheckPressureLongConcurrency(vector &entriesBatch) +{ + std::random_device randDevAnyKeyNo, randDevTag; + std::mt19937 genRandAnyKeyNo(randDevAnyKeyNo()), genRandTag(randDevTag()); + std::uniform_int_distribution disRandAnyKeyNo(ANY_RECORDS_NUM_START, ANY_RECORDS_NUM_END); + std::uniform_int_distribution disRandTag(static_cast(ReadOrWriteTag::READ), + static_cast(ReadOrWriteTag::REGISTER)); + unsigned int randKeyNo; + unsigned int threadCurId = 0; + int randFlag; + + std::chrono::time_point start, end; + std::chrono::duration dur; + double operInterval = 0.0; + + start = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + while (operInterval < static_cast(LONG_TIME_TEST_SECONDS)) { + auto threadParam = new (std::nothrow) ConcurParam; + ASSERT_NE(threadParam, nullptr); + threadParam->entryPtr_ = new (std::nothrow) DistributedDB::Entry; + ASSERT_NE(threadParam->entryPtr_, nullptr); + threadParam->threadId_ = threadCurId++; + randFlag = disRandTag(genRandTag); + threadParam->tag_ = static_cast(randFlag); + randKeyNo = disRandAnyKeyNo(genRandAnyKeyNo); + + Entry entryCurrent; + GenerateRecord(randKeyNo, entryCurrent); + + CheckPressureLongCompare(entriesBatch, threadParam, entryCurrent); + + threadParam->entryPtr_->key = entryCurrent.key; + threadParam->entryPtr_->value = entryCurrent.value; + std::thread thread = std::thread(ConcurOperThread, reinterpret_cast(threadParam)); + thread.join(); + + std::this_thread::sleep_for(std::chrono::duration(LONG_TIME_INTERVAL_MILLSECONDS)); + end = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + dur = std::chrono::duration_cast(end - start); + operInterval = static_cast(dur.count()) * chrono::microseconds::period::num + / chrono::microseconds::period::den; + delete threadParam->entryPtr_; + threadParam->entryPtr_ = nullptr; + delete threadParam; + threadParam = nullptr; + } +} + +/* + * @tc.name: Pressure 021 + * @tc.desc: Verify that can read and write Concurrency observer, and each observer will success + * @tc.type: LONGTIME TEST + * @tc.require: SR000CCPOI + * @tc.author: luqianfu + */ +HWTEST_F(DistributeddbNbObserverTest, Pressure021, TestSize.Level3) +{ + vector entriesBatch; + vector allKeys; + GenerateRecords(BATCH_RECORDS, DEFAULT_START, allKeys, entriesBatch); + + DBStatus status = DistributedDBNbTestTools::PutBatch(*g_nbObserverDelegate, entriesBatch); + EXPECT_TRUE(status == DBStatus::OK); + vector valueResult; + status = DistributedDBNbTestTools::GetEntries(*g_nbObserverDelegate, KEY_SEARCH_4, valueResult); + EXPECT_EQ(status, OK); + MST_LOG("value size %zu", valueResult.size()); + + /** + * @tc.steps: step1. start 6 threads to register 6 observers to crud on db and + * start 4 threads to register observer at the same time. + * @tc.expected: step1. each thread start successfully and each observer register successfully + * and could not effect each other. + */ + CheckPressureLongConcurrency(entriesBatch); + + while (entriesBatch.size() > 0) { + status = DistributedDBNbTestTools::Delete(*g_nbObserverDelegate, entriesBatch[0].key); + EXPECT_EQ(status, OK); + entriesBatch.erase(entriesBatch.begin()); + } + MST_LOG("entriesBatch.size() = %zu", entriesBatch.size()); +} + +/* + * @tc.name: RekeyNbDb 001 + * @tc.desc: verify that Rekey will return busy when there are registered observer or putting data to db. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbObserverTest, RekeyNbDb001, TestSize.Level2) +{ + KvStoreNbDelegate *nbObserverDelegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option; + + nbObserverDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && nbObserverDelegate != nullptr); + + /** + * @tc.steps: step1. register observer. + * @tc.expected: step1. register successfully. + */ + KvStoreObserverImpl observerLocal; + KvStoreObserverImpl observerSync; + DBStatus status = nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_LOCAL_ONLY, &observerLocal); + EXPECT_EQ(status, OK); + status = nbObserverDelegate->RegisterObserver(KEY_EMPTY, + OBSERVER_CHANGES_NATIVE | OBSERVER_CHANGES_FOREIGN, &observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. call Rekey to update passwd to passwd_1. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_TRUE(nbObserverDelegate->Rekey(g_passwd1) == BUSY); + + /** + * @tc.steps: step3. unregister observer. + * @tc.expected: step3. unregister successfully. + */ + status = nbObserverDelegate->UnRegisterObserver(&observerLocal); + EXPECT_EQ(status, OK); + status = nbObserverDelegate->UnRegisterObserver(&observerSync); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step4. put (1k,4M) of (k,v) to db. + * @tc.expected: step4. put successfully. + */ + std::mutex count; + vector entriesBatch; + vector allKeys; + GenerateFixedRecords(entriesBatch, allKeys, RECORDS_NUM_START, ONE_K_LONG_STRING, FOUR_M_LONG_STRING); + thread subThread([&]() { + DBStatus putStatus = nbObserverDelegate->Put(entriesBatch[0].key, entriesBatch[0].value); + EXPECT_TRUE(putStatus == OK || putStatus == BUSY); + }); + subThread.detach(); + + /** + * @tc.steps: step5. call Rekey to update passwd to passwd_2 when put data to db. + * @tc.expected: step5. Rekey returns BUSY. + */ + status = nbObserverDelegate->Rekey(g_passwd2); + EXPECT_TRUE(status == OK || status == BUSY); + std::this_thread::sleep_for(std::chrono::seconds(FIVE_SECONDS)); + EXPECT_TRUE(manager->CloseKvStore(nbObserverDelegate) == OK); + nbObserverDelegate = nullptr; + EXPECT_TRUE(manager->DeleteKvStore(STORE_ID_2) == OK); + delete manager; + manager = nullptr; +} + +/* + * @tc.name: RekeyNbDb 002 + * @tc.desc: verify that Rekey will return busy when there are opened conflict notifier. + * @tc.type: FUNC + * @tc.require: SR000CQDT4 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbObserverTest, RekeyNbDb002, TestSize.Level0) +{ + /** + * @tc.steps: step1. register conflict notifier. + * @tc.expected: step1. register successfully. + */ + ConflictNbCallback callback; + std::vector conflictData; + auto notifier = bind(&ConflictNbCallback::NotifyCallBack, &callback, placeholders::_1, &conflictData); + DBStatus status = g_nbObserverDelegate->SetConflictNotifier(CONFLICT_FOREIGN_KEY_ONLY, notifier); + EXPECT_TRUE(status == OK); + + /** + * @tc.steps: step2. call Rekey to update passwd to passwd_1. + * @tc.expected: step2. Rekey returns BUSY. + */ + EXPECT_TRUE(g_nbObserverDelegate->Rekey(g_passwd1) == BUSY); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_expand_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_expand_test.cpp new file mode 100755 index 000000000..d2d30c7ea --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_expand_test.cpp @@ -0,0 +1,1188 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "distributeddb_schema_test_tools.h" + +using namespace std; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbPredicateQueryExpand { +KvStoreNbDelegate *g_nbQueryDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; +const int CURSOR_POSITION_NEGATIVE1 = -1; +const int CURSOR_POSITION_1 = 1; + +DistributedDB::CipherPassword g_passwd1; + +class DistributeddbNbPredicateQueryExpandTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbPredicateQueryExpandTest::SetUpTestCase(void) +{ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); +} + +void DistributeddbNbPredicateQueryExpandTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbPredicateQueryExpandTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); + + string validSchema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_COMBINATION_DEFINE, VALID_INDEX_1); + Option option = g_option; + option.isMemoryDb = false; + option.schema = validSchema; + g_nbQueryDelegate = DistributedDBNbTestTools::GetNbDelegateSuccess(g_manager, g_dbParameter1, option); + ASSERT_TRUE(g_nbQueryDelegate != nullptr && g_manager != nullptr); +} + +void DistributeddbNbPredicateQueryExpandTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + ASSERT_NE(g_manager, nullptr); + EXPECT_TRUE(EndCaseDeleteDB(g_manager, g_nbQueryDelegate, STORE_ID_1, false)); +} + +vector GenerateCombinationSchemaValue(const vector> &fieldValue) +{ + vector schemasValue; + int valueNum = fieldValue.size(); + for (int index = 0; index < valueNum; index++) { + string valueStr; + string field1Val = fieldValue[index][INDEX_ZEROTH]; + if (field1Val != "null") { + valueStr = valueStr + "{\"field1\":\"" + field1Val + "\""; + } else { + valueStr = valueStr + "{\"field1\":" + field1Val; + } + // 1,2,3,4 is the index of fieldValue + valueStr = valueStr + ",\"field2\":{\"field3\":" + fieldValue[index][INDEX_FIRST] + ",\"field4\":{\"field5\":" + + fieldValue[index][INDEX_SECOND] + ",\"field6\":{\"field7\":" + fieldValue[index][INDEX_THIRD] + ",\"field8\":" + + fieldValue[index][INDEX_FORTH] + "}}}}"; + schemasValue.push_back(valueStr); + } + return schemasValue; +} + +void PresetDatasToDB(KvStoreNbDelegate *&delegate, std::vector keyPrefix, vector &schemasValue, + vector &entries) +{ + int num = schemasValue.size(); + for (int index = 1; index <= num; index++) { + DistributedDB::Entry entry; + std::string ind = std::to_string(index); + entry.key.push_back(keyPrefix[0]); + for (auto ch = ind.begin(); ch != ind.end(); ch++) { + entry.key.push_back(*ch); + } + Value value(schemasValue[index - 1].begin(), schemasValue[index - 1].end()); + entry.value = value; + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, entry.key, entry.value), OK); + entries.push_back(entry); + } +} + +bool CheckQueryResult(KvStoreNbDelegate &delegate, const Query &query, vector &expectEntry, + const DBStatus status, bool canGetCount) +{ + vector entries; + EXPECT_EQ(delegate.GetEntries(query, entries), status); + if (!expectEntry.empty()) { + if (entries.size() != expectEntry.size()) { + MST_LOG("The entries from query is %zd, The expectEntry is %zd, they are not equal", + entries.size(), expectEntry.size()); + return false; + } + for (vector::size_type index = 0; index < entries.size(); index++) { + if (entries[index].key != expectEntry[index].key || entries[index].value != expectEntry[index].value) { + string keyGot(entries[index].key.begin(), entries[index].key.end()); + string keyExpect(expectEntry[index].key.begin(), expectEntry[index].key.end()); + MST_LOG("entry key compare failed, expectKey:%s, gotKey:%s, line:%d", keyExpect.c_str(), keyGot.c_str(), + __LINE__); + return false; + } + } + } + KvStoreResultSet *resultSet = nullptr; + if (status != NOT_FOUND) { + EXPECT_EQ(delegate.GetEntries(query, resultSet), status); + } else { + EXPECT_EQ(delegate.GetEntries(query, resultSet), OK); + if (resultSet != nullptr) { + Entry entry; + EXPECT_EQ(resultSet->GetEntry(entry), status); + } + } + int expectCnt = expectEntry.size(); + if (resultSet != nullptr) { + EXPECT_EQ(resultSet->GetCount(), expectCnt); + EXPECT_EQ(delegate.CloseResultSet(resultSet), DBStatus::OK); + } + if (canGetCount) { + int cnt = 0; + EXPECT_EQ(delegate.GetCount(query, cnt), status); + EXPECT_EQ(cnt, expectCnt); + } + return true; +} + +/** + * @tc.name: BracketsTest 001 + * @tc.desc: Verify the Brackets can change priority of query methods if brackets appear in pairs. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, BracketsTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 7 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abxxx", "true", "12", "120", "-79"}, + {"ab", "true", "20", "82", "150.999"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. test the Query without brackets and check the result with GetEntries. + * @tc.expected: step2. GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field2.field3", true).And().LessThanOrEqualTo("$.field2.field4.field5", + 33).Or().LessThanOrEqualTo("$.field2.field4.field6.field7", 1000).And().NotLike("$.field1", "%c"). \ + OrderBy("$.field2.field4.field6.field8", true); + vector expectEntry1 = {entries[INDEX_FIRST], entries[INDEX_FIFTH], entries[INDEX_ZEROTH], + entries[INDEX_THIRD], entries[INDEX_SECOND], entries[INDEX_FORTH], entries[INDEX_SIXTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, expectEntry1, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with brackets appear in pairs and check the result with GetEntries. + * @tc.expected: step3. GetEntries return right result. + */ + Query query2 = Query::Select().BeginGroup().EqualTo("$.field2.field3", true).And(). \ + LessThanOrEqualTo("$.field2.field4.field5", 33).Or().LessThanOrEqualTo("$.field2.field4.field6.field7", 1000). \ + EndGroup().And().NotLike("$.field1", "%c").OrderBy("$.field2.field4.field6.field8", true); + vector expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_FIFTH], entries[INDEX_THIRD], + entries[INDEX_SECOND], entries[INDEX_FORTH], entries[INDEX_SIXTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, expectEntry2, DBStatus::OK, false)); + + /** + * @tc.steps: step4. test the Query with brackets appear in pairs and check the result with GetCount. + * @tc.expected: step4. GetCount return 7. + */ + Query query3 = Query::Select().BeginGroup().EqualTo("$.field2.field3", true).And(). \ + LessThanOrEqualTo("$.field2.field4.field5", 33).Or().LessThanOrEqualTo("$.field2.field4.field6.field7", 1000). \ + EndGroup().And().NotLike("$.field1", "%c"); + int cnt = 0; + int expectCnt = expectEntry2.size(); + EXPECT_EQ(g_nbQueryDelegate->GetCount(query3, cnt), OK); + EXPECT_EQ(cnt, expectCnt); +} + +/** + * @tc.name: BracketsTest 002 + * @tc.desc: Verify the Brackets can change priority of query methods if brackets nested appear. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, BracketsTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 7 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abxxx", "true", "12", "120", "-79"}, + {"ab", "true", "20", "82", "150.999"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. test the Query without brackets and check the result with GetEntries. + * @tc.expected: step2. GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field2.field3", true).And().LessThanOrEqualTo("$.field2.field4.field5", + 33).Or().LessThanOrEqualTo("$.field2.field4.field6.field7", 1000).And().NotLike("$.field1", "%c"). \ + OrderBy("$.field2.field4.field6.field8", true); + vector expectEntry1 = {entries[INDEX_FIRST], entries[INDEX_FIFTH], entries[INDEX_ZEROTH], + entries[INDEX_THIRD], entries[INDEX_SECOND], entries[INDEX_FORTH], entries[INDEX_SIXTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, expectEntry1, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with brackets nested appeared and check the result with GetEntries. + * @tc.expected: step3. GetEntries return right result. + */ + Query query2 = Query::Select().EqualTo("$.field2.field3", true).And().BeginGroup(). \ + LessThanOrEqualTo("$.field2.field4.field5", 33).Or().BeginGroup(). \ + LessThanOrEqualTo("$.field2.field4.field6.field7", 1000).And().NotLike("$.field1", "%c"). \ + EndGroup().EndGroup().OrderBy("$.field2.field4.field6.field8", true); + vector expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_FIFTH], entries[INDEX_ZEROTH], + entries[INDEX_SECOND], entries[INDEX_FORTH], entries[INDEX_SIXTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, expectEntry2, DBStatus::OK, false)); + + /** + * @tc.steps: step4. test the Query with brackets nested appeared and check the result with GetCount. + * @tc.expected: step4. GetCount return 6. + */ + Query query3 = Query::Select().EqualTo("$.field2.field3", true).And().BeginGroup(). \ + LessThanOrEqualTo("$.field2.field4.field5", 33).Or().BeginGroup(). \ + LessThanOrEqualTo("$.field2.field4.field6.field7", 1000).And().NotLike("$.field1", "%c").EndGroup().EndGroup(); + int cnt = 0; + int expectCnt = expectEntry2.size(); + EXPECT_EQ(g_nbQueryDelegate->GetCount(query3, cnt), OK); + EXPECT_EQ(cnt, expectCnt); +} +// insert query condition between brackets. +void SpliceQueryMethod(int flag, Query &query) +{ + switch (flag) { + case 0: // query condition 0 + query.LessThan("$.field2.field4.field5", "100").And().NotEqualTo("$.field2.field4.field6.field7", "-100"); + break; + case 1: // query condition 1 + query.EqualTo("$.field2.field3", "true").Or().NotLike("$.field1", "%c"); + break; + case 2: // query condition 2 + query.Like("$.field1", "ab%"); + break; + case 3: // query condition 3 + query.GreaterThanOrEqualTo("$.field2.field4.field6.field8", "0"); + break; + default: // other query condition + break; + } +} +// Generate rand query by the number of brackets. +void GenerateRandQuery(Query &query, int beginNum, int endNum) +{ + int left = 0; + int right = 0; + for (int cnt = 0; cnt < beginNum + endNum; cnt++) { + int brachetTest = GetRandInt(0, 1); + if (brachetTest == 0 && left < beginNum) { + left++; + if (cnt != 0) { + query.Or(); + } + query.BeginGroup(); + } else if (brachetTest == 1 && right < endNum) { + right++; + query.EndGroup(); + } else { + cnt--; + continue; + } + int flag = GetRandInt(0, 4); // add query condition 0-4 to brackets. + if ((brachetTest == 0 && cnt < beginNum + endNum - 1) || (brachetTest == 1 && cnt == 0)) { + SpliceQueryMethod(flag, query); + } else if (brachetTest == 1 && cnt < beginNum + endNum - 1) { + query.And(); + SpliceQueryMethod(flag, query); + } + } +} +/** + * @tc.name: BracketsTest 003 + * @tc.desc: Verify the Brackets appear abnormally then call predicate query interface will return INVALID_QUERY_FORMAT. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, BracketsTest003, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 7 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abxxx", "true", "12", "120", "-79"}, + {"ab", "true", "20", "82", "150.999"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. generate query that the right bracket is more than left bracket,then call GetEntries()/ + * GetCount(). + * @tc.expected: step2. call GetEntries()/GetCount() return INVALID_QUERY_FORMAT. + */ + Query query1 = Query::Select(); + int bracketNum1 = GetRandInt(0, 5); // generate the number of bracket from 0 to 5 randomly. + GenerateRandQuery(query1, bracketNum1, bracketNum1 + 1); + vector expectEntry; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); + + /** + * @tc.steps: step3. generate query that the right bracket is less than left bracket,then call GetEntries()/ + * GetCount(). + * @tc.expected: step3. call GetEntries()/GetCount() return INVALID_QUERY_FORMAT. + */ + Query query2 = Query::Select(); + int bracketNum2 = GetRandInt(1, 5); // generate the number of bracket from 1 to 5 randomly. + GenerateRandQuery(query2, bracketNum2, bracketNum2 - 1); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); +} + +/** + * @tc.name: BracketsTest 004 + * @tc.desc: Verify test the Query with brackets that having And() after BeginGroup() or having Or() before EndGroup() + * will return INVALID_QUERY_FORMAT. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, BracketsTest004, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 7 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abxxx", "true", "12", "120", "-79"}, + {"ab", "true", "20", "82", "150.999"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. test the Query with brackets that having And() after BeginGroup() and check the result + * with GetEntries/GetCount. + * @tc.expected: step2. GetEntries and GetCount return INVALID_QUERY_FORMAT. + */ + Query query1 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().BeginGroup().And(). \ + EqualTo("$.field2.field3", true).Or().Like("$.field1", "ab%").EndGroup(); + vector expectEntry; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); + + /** + * @tc.steps: step3. test the Query with brackets that having or having Or() before EndGroup() and check the result + * with GetEntries/GetCount. + * @tc.expected: step3. GetEntries and GetCount return INVALID_QUERY_FORMAT. + */ + Query query2 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().BeginGroup(). \ + EqualTo("$.field2.field3", true).Or().Like("$.field1", "ab%").Or().EndGroup(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); + + /** + * @tc.steps: step4. test the Query with brackets that right bracket before left bracket. + * @tc.expected: step4. GetEntries and GetCount return INVALID_QUERY_FORMAT. + */ + Query query3 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().EndGroup(). \ + EqualTo("$.field2.field3", true).Or().Like("$.field1", "ab%").Or().BeginGroup(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); +} + +/** + * @tc.name: BracketsTest 005 + * @tc.desc: Verify query with OrderBy/Limit in brackets will return INVALID_QUERY_FORMAT. + * @tc.type: FUNC + * @tc.require: + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, BracketsTest005, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 7 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abxxx", "true", "12", "120", "-79"}, + {"ab", "true", "20", "82", "150.999"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. test the Query with OrderBy in the brackets and check the result. + * with GetEntries/GetCount. + * @tc.expected: step2. GetEntries and GetCount return INVALID_QUERY_FORMAT. + */ + Query query1 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().BeginGroup(). \ + EqualTo("$.field2.field3", true).Or().Like("$.field1", "ab%").OrderBy("$.field2.field4.field6.field8", true). \ + EndGroup(); + vector expectEntry; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, expectEntry, DBStatus::INVALID_QUERY_FORMAT, true)); + + /** + * @tc.steps: step3. test the Query with Limit in the brackets and check the result + * @tc.expected: step3. GetEntries and GetCount return INVALID_QUERY_FORMAT. + */ + Query query2 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().BeginGroup(). \ + EqualTo("$.field2.field3", true).Or().Like("$.field1", "ab%").Limit(5, 0).EndGroup(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, expectEntry, DBStatus::INVALID_QUERY_FORMAT, false)); +} + +/** + * @tc.name: NotNullTest 001 + * @tc.desc: Verify query IsNotNull can get right result when put right param. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, NotNullTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"null", "true", "9", "1000", "12"}, {"abc123", "null", "88", "-100", "-99"}, + {"abfxy", "true", "null", "0", "38"}, {"ab789", "false", "999", "null", "15.8"}, + {"ab000", "true", "33", "30", "null"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + vector> expectEntries = {entries, entries, entries, entries, entries}; + expectEntries[0].erase(expectEntries[0].begin()); + expectEntries[INDEX_FIRST].erase(expectEntries[INDEX_FIRST].begin() + INDEX_FIRST); + expectEntries[INDEX_SECOND].erase(expectEntries[INDEX_SECOND].begin() + INDEX_SECOND); + expectEntries[INDEX_THIRD].erase(expectEntries[INDEX_THIRD].begin() + INDEX_THIRD); + expectEntries[INDEX_FORTH].pop_back(); + + /** + * @tc.steps: step2. test the Query with IsNotNull interface to test each field. + * @tc.expected: step2. construct success. + */ + vector queries; + queries.push_back(Query::Select().IsNotNull("$.field1")); + queries.push_back(Query::Select().IsNotNull("$.field2.field3")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field5")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field7")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field8")); + /** + * @tc.steps: step3. call GetEntries(query, Entries), GetEntries(query, resultSet), and GetCount() + * to check the query + * @tc.expected: step3. GetEntries success and each query can return right result. + */ + for (vector::size_type index = 0; index < queries.size(); index++) { + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, queries[index], expectEntries[index], DBStatus::OK, true)); + } +} + +/** + * @tc.name: NotNullTest 002 + * @tc.desc: Verify query IsNotNull can get right result when put right param. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, NotNullTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entries, entries2; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entries); + + /** + * @tc.steps: step2. test the Query with IsNotNull interface to test each field. + * @tc.expected: step2. construct success. + */ + vector queries; + queries.push_back(Query::Select().IsNotNull("$.field1")); + queries.push_back(Query::Select().IsNotNull("$.field2.field3")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field5")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field7")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field8")); + /** + * @tc.steps: step3. call GetEntries(query, Entries), GetEntries(query, resultSet), and GetCount() + * to check the query + * @tc.expected: step3. GetEntries success and each query can return right result. + */ + for (vector::size_type index = 0; index < queries.size(); index++) { + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, queries[index], entries, DBStatus::OK, true)); + } + + /** + * @tc.steps: step4. delete all the 5 entries and insert new 5 records that include null element. + * @tc.expected: step4. delete and insert success. + */ + vector keys; + for (vector::iterator iter = entries.begin(); iter != entries.end(); iter++) { + keys.push_back(iter->key); + } + EXPECT_EQ(DistributedDBNbTestTools::DeleteLocalBatch(*g_nbQueryDelegate, keys), OK); + + vector> fieldValue2 = { + {"null", "null", "null", "null", "null"}, {"null", "null", "null", "null", "null"}, + {"null", "null", "null", "null", "null"}, {"null", "null", "null", "null", "null"}, + {"null", "null", "null", "null", "null"}}; + vector schemasValue2 = GenerateCombinationSchemaValue(fieldValue2); + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue2, entries2); + + /** + * @tc.steps: step5. get the Query again to check the IsNotNull interface. + * @tc.expected: step5. query success. + */ + queries.clear(); + queries.push_back(Query::Select().IsNotNull("$.field1")); + queries.push_back(Query::Select().IsNotNull("$.field2.field3")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field5")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field7")); + queries.push_back(Query::Select().IsNotNull("$.field2.field4.field6.field8")); + /** + * @tc.steps: step6. call GetEntries(query, Entries), GetEntries(query, resultSet), and GetCount() + * to check the query + * @tc.expected: step6. GetEntries success and each query can return right result. + */ + vector> expectEntries = {{}, {}, {}, {}, {}}; + for (vector::size_type index = 0; index < queries.size(); index++) { + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, queries[index], expectEntries[index], + DBStatus::NOT_FOUND, true)); + } +} + +/** + * @tc.name: PrefixKeyTest 001 + * @tc.desc: Verify that PrefixKey interface can return right records. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 10 entries 5 of which has prefixKey {'k'} and rest of them has + * prefixKey {'a'}. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesExpect, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey('') interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 10 records. + */ + for (vector::size_type index = 0; index < entriesA.size(); index++) { + entriesExpect.push_back(entriesA[index]); + } + for (vector::size_type index = 0; index < entriesK.size(); index++) { + entriesExpect.push_back(entriesK[index]); + } + Query query1 = Query::Select().PrefixKey({}); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, entriesExpect, DBStatus::OK, true)); + + /** + * @tc.steps: step3. test the Query with PrefixKey({'k'}) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 5 records. + */ + Query query2 = Query::Select().PrefixKey({'k'}); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, entriesK, DBStatus::OK, true)); + /** + * @tc.steps: step4. test the Query with PrefixKey({'a'}) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query success and all of the 3 interface can find 5 records. + */ + Query query3 = Query::Select().PrefixKey({'b'}); + entriesExpect.clear(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, entriesExpect, DBStatus::NOT_FOUND, true)); +} + +/** + * @tc.name: PrefixKeyTest 002 + * @tc.desc: Verify that none-schema DB use PrefixKey can return right result + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest002, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(delegate != nullptr && manager != nullptr); + /** + * @tc.steps: step1. put 10 records to DB, 5 of which has the prefix key 'k', and rest of which has prefix key 'a'; + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesExpect, entriesGot; + PresetDatasToDB(delegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(delegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey({}) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 10 records. + */ + for (vector::size_type index = 0; index < entriesA.size(); index++) { + entriesExpect.push_back(entriesA[index]); + } + for (vector::size_type index = 0; index < entriesK.size(); index++) { + entriesExpect.push_back(entriesK[index]); + } + Query query1 = Query::Select().PrefixKey({}); + EXPECT_TRUE(CheckQueryResult(*delegate, query1, entriesExpect, DBStatus::OK, true)); + + /** + * @tc.steps: step3. test the Query with PrefixKey({'k'}) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 5 records. + */ + Query query2 = Query::Select().PrefixKey({'k'}); + EXPECT_TRUE(CheckQueryResult(*delegate, query2, entriesK, DBStatus::OK, true)); + /** + * @tc.steps: step4. test the Query with PrefixKey({'b'}) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query success and all of the 3 interface can find 0 records. + */ + entriesA.clear(); + Query query3 = Query::Select().PrefixKey({'b'}); + EXPECT_TRUE(CheckQueryResult(*delegate, query3, entriesA, DBStatus::NOT_FOUND, true)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: PrefixKeyTest 003 + * @tc.desc: Verify that use PrefixKey check schema DB can return right result, and prefixKey can't be call many time + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest003, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 10 entries 5 of which has prefixKey {'k'} and rest of them has + * prefixKey {'a'}. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey({}).Limit(5, 0) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 5 records. + */ + Query query1 = Query::Select().PrefixKey({}).Limit(5, 0); + vector entriesCheck = {entriesA[0], entriesA[INDEX_FIRST], entriesA[INDEX_SECOND], + entriesA[INDEX_THIRD], entriesA[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, entriesCheck, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with PrefixKey({}).And().GreaterThan("$.field2.field4.field5", 10).Limit(5, 0) + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 4 records. + */ + Query query2 = Query::Select().PrefixKey({}).GreaterThan("$.field2.field4.field5", "10").Limit(5, 0); + entriesCheck = {entriesA[INDEX_FIRST], entriesA[INDEX_THIRD], entriesA[INDEX_FORTH], + entriesK[INDEX_FIRST], entriesK[INDEX_THIRD]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, entriesCheck, DBStatus::OK, false)); + /** + * @tc.steps: step4. test the Query with PrefixKey({}).And().PrefixKey({'k'}).And().PrefixKey({'b'}) + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query failed and returned INVALID_QUERY_FORMAT. + */ + Query query3 = Query::Select().PrefixKey({}).PrefixKey({'k'}).And().PrefixKey({'b'}); + entriesCheck.clear(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, entriesCheck, DBStatus::INVALID_QUERY_FORMAT, false)); +} + +/** + * @tc.name: PrefixKeyTest 004 + * @tc.desc: Verify that none-schema DB can't use query interface except PrefixKey and Limit. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest004, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(delegate != nullptr && manager != nullptr); + /** + * @tc.steps: step1. put 10 records to DB, 5 of which has the prefix key 'k', and rest of which has prefix key 'a'; + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesExpect, entriesGot; + PresetDatasToDB(delegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(delegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey('').Limit(5, 0) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 5 records. + */ + Query query1 = Query::Select().PrefixKey({ }).Limit(5, 0); + vector entriesCheck = {entriesA[0], entriesA[INDEX_FIRST], entriesA[INDEX_SECOND], + entriesA[INDEX_THIRD], entriesA[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*delegate, query1, entriesCheck, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with PrefixKey({}).And().GreaterThan("$.field2.field4.field5", 10).Limit(5, 0) + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 4 records. + */ + Query query2 = Query::Select().PrefixKey({}).GreaterThan("$.field2.field4.field5", 10).Limit(5, 0); + entriesCheck.clear(); + EXPECT_TRUE(CheckQueryResult(*delegate, query2, entriesCheck, DBStatus::NOT_SUPPORT, false)); + /** + * @tc.steps: step4. test the Query with PrefixKey({}).And().PrefixKey({'k'}).And().PrefixKey({'b'}) + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query failed and returned INVALID_QUERY_FORMAT. + */ + Query query3 = Query::Select().PrefixKey({}).PrefixKey({'k'}).PrefixKey({'b'}); + EXPECT_TRUE(CheckQueryResult(*delegate, query3, entriesCheck, DBStatus::INVALID_QUERY_FORMAT, false)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: PrefixKeyTest 005 + * @tc.desc: Verify that schema DB can use prefixKey and other interface constructor legal query + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest005, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 10 entries 5 of which has prefixKey {'k'} and rest of them has + * prefixKey {'a'}. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey({}).And().EqualTo("$.field2.field3",true).OrderBy("field8") + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 8 records. + */ + Query query1 = Query::Select().PrefixKey({}).EqualTo("$.field2.field3", + true).OrderBy("$.field2.field4.field6.field8"); + vector entriesExpect = {entriesA[INDEX_FIRST], entriesK[INDEX_FIRST], entriesA[0], entriesK[0], + entriesA[INDEX_SECOND], entriesK[INDEX_SECOND], entriesA[INDEX_FORTH], entriesK[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, entriesExpect, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with NotEqualTo("$.field2.field3",false).And().PrefixKey({}).OrderBy("field8") + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 8 records. + */ + Query query2 = Query::Select().NotEqualTo("$.field2.field3", false).PrefixKey({}). \ + OrderBy("$.field2.field4.field6.field8"); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, entriesExpect, DBStatus::OK, false)); + + /** + * @tc.steps: step4. test the Query with LessThan("$.field2.field4.field6.field8", + * 149).And().PrefixKey({}).Limit(3, 0) interface and then check the query use GetEntries(query, Entries), + * GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query successfully and returned {entriesA[0], entriesA[1], entriesA[2]}. + */ + Query query3 = Query::Select().LessThan("$.field2.field4.field6.field8", 149).PrefixKey({}).Limit(3, 0); + entriesExpect = {entriesA[0], entriesA[INDEX_FIRST], entriesA[INDEX_SECOND]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step5. test the Query with GreaterThanOrEqualTo("$.field2.field4.field5", 9).And().PrefixKey({'a'}). + * LessThanOrEqualTo("field7", 50).OrderBy("$.field2.field4.field6.field8").Limit(4, 0) interface and then + * check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step5. query failed and can't find any records. + */ + Query query4 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 9).PrefixKey({'a'}).And(). \ + LessThanOrEqualTo("$.field2.field4.field6.field7", 50).OrderBy("$.field2.field4.field6.field8").Limit(4, 0); + entriesExpect = {entriesA[INDEX_FIRST], entriesA[INDEX_THIRD], entriesA[INDEX_SECOND], entriesA[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query4, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step6. test the Query with GreaterThanOrEqualTo("$.field2.field4.field5", 9).And().PrefixKey({'b'}). + * LessThanOrEqualTo("field7", 50).OrderBy("$.field2.field4.field6.field8").Limit(4, 0) interface and then + * check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step6. query failed and can't find any records. + */ + Query query5 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 9).PrefixKey({'b'}).And(). \ + LessThanOrEqualTo("$.field2.field4.field6.field7", 50).OrderBy("$.field2.field4.field6.field8"). \ + OrderBy("$.field1"); + entriesExpect = {}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query5, entriesExpect, DBStatus::NOT_FOUND, false)); +} + +/** + * @tc.name: PrefixKeyTest 006 + * @tc.desc: Verify that schema DB can use prefixKey and other interface constructor legal query + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest006, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 10 entries 5 of which has prefixKey {'k'} and rest of them has + * prefixKey {'a'}. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"null", "true", "9", "1000", "12"}, {"abc123", "null", "88", "-100", "-99"}, + {"abfxy", "true", "null", "0", "38"}, {"ab789", "false", "999", "null", "15.8"}, + {"ab000", "true", "33", "30", "null"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with PrefixKey({'b'}).And().Like("$.field1", ab%).OrderBy("field8") + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 8 records. + */ + Query query1 = Query::Select().PrefixKey({'b'}).Like("$.field1", "ab%"). \ + OrderBy("$.field2.field4.field6.field8"); + vector entriesExpect; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, entriesExpect, DBStatus::NOT_FOUND, false)); + + /** + * @tc.steps: step3. test the Query with NotLike("$.field1", ab%).And().PrefixKey({}).OrderBy("field8") + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 8 records. + */ + Query query2 = Query::Select().NotLike("$.field1", "ab%").PrefixKey({}). \ + OrderBy("$.field2.field4.field6.field8"); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, entriesExpect, DBStatus::NOT_FOUND, false)); + /** + * @tc.steps: step4. test the Query with In("$.field2.field4.field6.field7", + * [0, 30, 50, 1000]).Limit(3, 3) interface and then check the query use GetEntries(query, Entries), + * GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query successfully and returned {entriesA[0], entriesA[1], entriesA[2]}. + */ + vector scope = {"0", "30", "50", "1000"}; + Query query3 = Query::Select().PrefixKey({}).In("$.field2.field4.field6.field7", scope).Limit(3, 3); + entriesExpect = {entriesK[0], entriesK[INDEX_SECOND], entriesK[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step5. test the Query with NotIn("$.field2.field4.field6.field7", [-100]).And().PrefixKey({}). + * Limit(3, 3) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step5. query failed and can't find any records. + */ + scope = {"-100"}; + Query query4 = Query::Select().NotIn("$.field2.field4.field6.field7", scope).PrefixKey({}).Limit(3, 3); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query4, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step6. test the Query with IsNull("$.field2.field4.field5").And().PrefixKey({}). + IsNotNull("$.field2.field3").OrderBy("$.field2.field4.field6.field8") interface + * and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step6. query failed and can't find any records. + */ + Query query5 = Query::Select().IsNull("$.field2.field4.field5").And().PrefixKey({}). \ + IsNotNull("$.field2.field3").OrderBy("$.field2.field4.field6.field8"); + entriesExpect = {entriesA[INDEX_SECOND], entriesK[INDEX_SECOND]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query5, entriesExpect, DBStatus::OK, false)); +} + +/** + * @tc.name: PrefixKeyTest 007 + * @tc.desc: the query interface that without any condition can get all the records. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest007, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(delegate != nullptr && manager != nullptr); + /** + * @tc.steps: step1. create schema db and non-schema DB and put 10 entries to them. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}, {"abc", "true", "9", "1000", "12"}, + {"abc123", "true", "88", "-100", "-99"}, {"abfxy", "true", "10", "0", "38"}, + {"ab789", "false", "999", "50", "15.8"}, {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesSchema, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesSchema); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*delegate, entriesSchema), OK); + + /** + * @tc.steps: step2. test the Query Select() from the schema DB and then check the query + * use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 10 records. + */ + Query query = Query::Select(); + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query, entriesSchema, DBStatus::OK, true)); + + /** + * @tc.steps: step3. test the Query Select() from the non-schema DB and then check the query + * use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 10 records. + */ + EXPECT_TRUE(CheckQueryResult(*delegate, query, entriesSchema, DBStatus::OK, true)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: PrefixKeyTest 008 + * @tc.desc: the query that the position of prefixKey do not effect the result. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest008, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db and put 10 entries 5 of which has prefixKey {'k'} and rest of them has + * prefixKey {'a'}. + * @tc.expected: step1. create and put successfully. + */ + vector> fieldValue = { + {"abc", "true", "9", "1000", "12"}, {"abc123", "true", "88", "-100", "-99"}, + {"abfxy", "true", "10", "0", "38"}, {"ab789", "false", "999", "50", "15.8"}, + {"ab000", "true", "33", "30", "149"}}; + vector schemasValue = GenerateCombinationSchemaValue(fieldValue); + vector entriesK, entriesA, entriesGot; + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + + /** + * @tc.steps: step2. test the Query with Like("$.field1", "ab%").OrderBy("$.field2.field4.field6.field8"). + * PrefixKey({'a'}).Limit(5, 1) interface and then check the query use GetEntries(query, Entries), + * GetEntries(query, resultSet), GetCount(). + * @tc.expected: step2. query success and all of the 3 interface can find 4 records. + */ + Query query1 = Query::Select().Like("$.field1", "ab%").OrderBy("$.field2.field4.field6.field8"). + PrefixKey({'a'}).Limit(5, 1); + vector entriesExpect = {entriesA[INDEX_ZEROTH], entriesA[INDEX_THIRD], entriesA[INDEX_SECOND], + entriesA[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query1, entriesExpect, DBStatus::OK, false)); + + /** + * @tc.steps: step3. test the Query with NotLike("$.field1", ab%).And().PrefixKey({}).OrderBy("field8") + * interface and then check the query use GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step3. query success and all of the 3 interface can find 8 records. + */ + Query query2 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 9).And(). + LessThanOrEqualTo("$.field2.field4.field6.field7", 50).OrderBy("$.field2.field4.field6.field8"). + PrefixKey({'a'}).Limit(4, 0); + entriesExpect = {entriesA[INDEX_FIRST], entriesA[INDEX_THIRD], entriesA[INDEX_SECOND], entriesA[INDEX_FORTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query2, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step4. test the Query with GreaterThanOrEqualTo("$.field2.field4.field5", 9).And(). + * LessThanOrEqualTo("$.field2.field4.field6.field7", 50).OrderBy("$.field1").PrefixKey({'a'}). + * OrderBy("$.field2.field4.field6.field8") interface and then check the query use GetEntries(query, Entries), + * GetEntries(query, resultSet), GetCount(). + * @tc.expected: step4. query successfully and returned {entriesA[0], entriesA[1], entriesA[2]}. + */ + Query query3 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 9).And(). + LessThanOrEqualTo("$.field2.field4.field6.field7", 50).OrderBy("$.field1").PrefixKey({'a'}). + OrderBy("$.field2.field4.field6.field8"); + entriesExpect = {entriesA[INDEX_FORTH], entriesA[INDEX_THIRD], entriesA[INDEX_FIRST], entriesA[INDEX_SECOND]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query3, entriesExpect, DBStatus::OK, false)); + /** + * @tc.steps: step5. test the Query with NotIn("$.field2.field4.field6.field7", [-100]).And().PrefixKey({}). + * Limit(3, 3) interface and then check the query use + * GetEntries(query, Entries), GetEntries(query, resultSet), GetCount(). + * @tc.expected: step5. query failed and can't find any records. + */ + fieldValue = { + {"abc", "true", "33", "1000", "12"}, {"abc", "true", "9", "-100", "-99"}, + {"ab789", "true", "100", "0", "38"}, {"ab789", "false", "99", "50", "15.8"}, + {"abc", "true", "9", "30", "149"}}; + schemasValue = GenerateCombinationSchemaValue(fieldValue); + entriesA.clear(); + entriesK.clear(); + PresetDatasToDB(g_nbQueryDelegate, KEY_A, schemasValue, entriesA); + PresetDatasToDB(g_nbQueryDelegate, KEY_K, schemasValue, entriesK); + + Query query4 = Query::Select().OrderBy("$.field1").OrderBy("$.field2.field4.field5"). + OrderBy("$.field2.field4.field6.field8").PrefixKey({'a'}).Limit(3, 3); + entriesExpect = {entriesA[INDEX_FORTH], entriesA[INDEX_ZEROTH]}; + EXPECT_TRUE(CheckQueryResult(*g_nbQueryDelegate, query4, entriesExpect, DBStatus::OK, false)); +} + +void MoveCursor(KvStoreResultSet &resultSet, const vector &entriesBatch) +{ + int currentPosition = CURSOR_POSITION_NEGATIVE1; + Entry entry; + bool result; + for (int position = CURSOR_POSITION_NEGATIVE1; position < TEN_RECORDS; ++position) { + result = resultSet.MoveToNext(); + if (position < (TEN_RECORDS - CURSOR_POSITION_1)) { + EXPECT_TRUE(result); + } else { + EXPECT_TRUE(result == false); + } + currentPosition = resultSet.GetPosition(); + EXPECT_TRUE(currentPosition == (position + CURSOR_POSITION_1)); + if (position < (TEN_RECORDS - CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet.GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entriesBatch[position + CURSOR_POSITION_1].key)); + EXPECT_TRUE(CompareVector(entry.value, entriesBatch[position + CURSOR_POSITION_1].value)); + } else { + EXPECT_TRUE(resultSet.GetEntry(entry) == NOT_FOUND); + } + } + /** + * @tc.steps: step3. set the current position is 10, call MoveToPrevious, GetPostion, GetEntry looply. + * @tc.expected: step3. return values are all right. + */ + for (int position = TEN_RECORDS; position > CURSOR_POSITION_NEGATIVE1; --position) { + result = resultSet.MoveToPrevious(); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(result); + } else { + EXPECT_TRUE(result == false); + } + currentPosition = resultSet.GetPosition(); + EXPECT_TRUE(currentPosition == (position - CURSOR_POSITION_1)); + if (position > (CURSOR_POSITION_NEGATIVE1 + CURSOR_POSITION_1)) { + EXPECT_TRUE(resultSet.GetEntry(entry) == OK); + EXPECT_TRUE(CompareVector(entry.key, entriesBatch[position - CURSOR_POSITION_1].key)); + EXPECT_TRUE(CompareVector(entry.value, entriesBatch[position - CURSOR_POSITION_1].value)); + } else { + EXPECT_TRUE(resultSet.GetEntry(entry) == NOT_FOUND); + } + } +} +/* + * @tc.name: PrefixKeyTest 009 + * @tc.desc: test MoveToNext, MoveToPrevious interface with 10 2M data by query= prefixkey. + * @tc.type: FUNC + * @tc.require: SR000EPA23 + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryExpandTest, PrefixKeyTest009, TestSize.Level2) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, PERF_SCHEMA_DEFINE, + PERF_SCHEMA_SIX_INDEXES, SKIP_SIZE); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_NE(manager, nullptr); + ASSERT_NE(delegate, nullptr); + + vector entriesBatch; + vector allKeys; + EntrySize entrySize = {KEY_EIGHT_BYTE, TWO_M_LONG_STRING}; + entriesBatch = DistributedDBSchemaTestTools::GenerateFixedSchemaRecords(allKeys, TEN_RECORDS, entrySize, 'k', '0'); + EXPECT_EQ(DistributedDBNbTestTools::PutBatch(*delegate, entriesBatch), OK); + + /** + * @tc.steps: step1. call GetEntries interface to get KvStoreResultSet by query. + * @tc.expected: step1. get success. + */ + Query query = Query::Select().GreaterThanOrEqualTo("$.field1", 0).PrefixKey({'k'}); + KvStoreResultSet *resultSet = nullptr; + EXPECT_TRUE(delegate->GetEntries(query, resultSet) == OK); + sort(entriesBatch.begin(), entriesBatch.end(), DistributedTestTools::CompareKey); + /** + * @tc.steps: step2. set the current position is -1, call MoveToNext, GetPostion and GetEntry interface looply. + * @tc.expected: step2. return values are all right. + */ + ASSERT_NE(resultSet, nullptr); + MoveCursor(*resultSet, entriesBatch); + EXPECT_TRUE(delegate->CloseResultSet(resultSet) == OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, false)); +} +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_test.cpp new file mode 100755 index 000000000..89f998666 --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_predicate_query_test.cpp @@ -0,0 +1,2083 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#include + +#include "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "distributeddb_schema_test_tools.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbPredicateQuery { +KvStoreNbDelegate *g_nbQueryDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; +Option g_predicateOption; +const int IN_AND_NOTIN_MAX_LENGTH = 128; +const int LIKE_AND_NOTLIKE_MAX_LENGTH = 50000; + +DistributedDB::CipherPassword g_passwd1; +const std::string VALID_DEFINE_STRING = "{\"field1\":\"STRING ,DEFAULT null\",\"field2\":" + "{\"field3\":\"STRING ,DEFAULT null\",\"field4\":[]}}"; +const std::string VALID_DEFINE_BOOL = "{\"field1\": \"BOOL ,DEFAULT null\",\"field2\":" + "{\"field3\": \"BOOL ,DEFAULT null\",\"field4\":[]}}"; +const std::string VALID_DEFINE_INT = "{\"field1\": \"INTEGER ,DEFAULT null\",\"field2\":" + "{\"field3\": \"INTEGER ,DEFAULT null\",\"field4\":[]}}"; +const std::string VALID_DEFINE_LONG = "{\"field1\": \"LONG ,DEFAULT null\",\"field2\":" + "{\"field3\": \"LONG ,DEFAULT null\",\"field4\":[]}}"; +const std::string VALID_DEFINE_DOUBLE = "{\"field1\": \"DOUBLE ,DEFAULT null\",\"field2\":" + "{\"field3\": \"DOUBLE ,DEFAULT null\",\"field4\": []}}"; + +const int SCHEMA_GOT_COUNT_1 = 1; +const int SCHEMA_GOT_COUNT_2 = 2; +const int SCHEMA_GOT_COUNT_3 = 3; +const int SCHEMA_GOT_COUNT_4 = 4; +const int SCHEMA_GOT_COUNT_5 = 5; +const int CURSOR_OFFSET_2 = 2; +const int NO_RECORD = 0; + +const int CURSOR_POSITION_NEGATIVE1 = -1; +const int INTERFACE_QTY_2 = 2; +const int INTERFACE_QTY_6 = 6; +class DistributeddbNbPredicateQueryTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbPredicateQueryTest::SetUpTestCase(void) +{ + (void)g_passwd1.SetValue(PASSWD_VECTOR_1.data(), PASSWD_VECTOR_1.size()); +} + +void DistributeddbNbPredicateQueryTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbPredicateQueryTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbNbPredicateQueryTest::TearDown(void) +{ + RemoveDir(NB_DIRECTOR); +} + +void PrepareSchemaDBAndData(KvStoreNbDelegate *&delegate, KvStoreDelegateManager *&manager, + vector &schemasValue, vector &entries, const std::string schemaDefine) +{ + string validSchema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, VALID_INDEX_1); + + g_predicateOption = g_option; + g_predicateOption.isMemoryDb = false; + g_predicateOption.schema = validSchema; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, g_predicateOption); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + vector keys; + if (schemasValue.size() == 5) { // 5 elements + keys = {KEY_1, KEY_2, KEY_3, KEY_4, KEY_5}; + } else if (schemasValue.size() == 7) { // 7 elements + keys = {KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7}; + } else if (schemasValue.size() == 10) { // 10 elements + keys = {KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_10}; + } + for (unsigned long index = 0; index < schemasValue.size(); index++) { + Value value(schemasValue[index].begin(), schemasValue[index].end()); + entries.push_back({keys[index], value}); + } + + for (unsigned long index = 0; index < schemasValue.size(); index++) { + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, entries[index].key, entries[index].value), OK); + } +} + +bool CheckSchemaQuery(KvStoreNbDelegate *&delegate, Query &queryNeedCheck, vector &expectEntry, + const int &expectCount, DBStatus status) +{ + bool result = true; + vector entries; + int count = 0; + DBStatus statusGot = delegate->GetEntries(queryNeedCheck, entries); + result = (statusGot == status) && result; + MST_LOG("GetEntries entries result: %d, statusGot: %d", result, statusGot); + for (unsigned long index = 0; index < entries.size(); index++) { + if (!expectEntry.empty()) { + result = (entries[index].key == expectEntry[index].key) && result; + MST_LOG("check entries[%ld] key result:%d", index, result); + result = (entries[index].value == expectEntry[index].value) && result; + } else { + MST_LOG("expect value is empty, but Got has: %zu entries", entries.size()); + return false; + } + } + statusGot = delegate->GetCount(queryNeedCheck, count); + result = (statusGot == status) && result; + MST_LOG("GetCount result: %d, statusGot: %d", result, statusGot); + result = (count == expectCount) && result; + MST_LOG("Is GotCount equal to Expect result: %d", result); + KvStoreResultSet *resultSet = nullptr; + if (status != DBStatus::NOT_FOUND) { + statusGot = delegate->GetEntries(queryNeedCheck, resultSet); + result = (statusGot == status) && result; + } else { + statusGot = delegate->GetEntries(queryNeedCheck, resultSet); + result = (statusGot == DBStatus::OK) && result; + } + MST_LOG("GetEntries resultSet result: %d, statusGot: %d", result, statusGot); + if (resultSet != nullptr) { + int quatity = resultSet->GetCount(); + result = (quatity == expectCount) && result; + MST_LOG("ResultSet GetCount result: %d, quatity: %d", result, quatity); + result = (delegate->CloseResultSet(resultSet) == DBStatus::OK) && result; + } + return result; +} + +bool CheckSchemaQueryForOrderBy(KvStoreNbDelegate *&delegate, Query &queryNeedCheck, vector &expectEntry, + const int &expectCount, DBStatus status) +{ + bool result = true; + vector entries; + DBStatus statusGot = delegate->GetEntries(queryNeedCheck, entries); + result = (statusGot == status) && result; + MST_LOG("GetEntries entries result: %d, statusGot: %d", result, statusGot); + for (unsigned long index = 0; index < entries.size(); index++) { + if (!expectEntry.empty()) { + result = (entries[index].key == expectEntry[index].key) && result; + MST_LOG("check entries[%ld] key result:%d", index, result); + result = (entries[index].value == expectEntry[index].value) && result; + } else { + MST_LOG("expect value is empty, but Got: %zu entries", entries.size()); + } + } + KvStoreResultSet *resultSet = nullptr; + if (status != DBStatus::NOT_FOUND) { + statusGot = delegate->GetEntries(queryNeedCheck, resultSet); + result = (statusGot == status) && result; + } else { + statusGot = delegate->GetEntries(queryNeedCheck, resultSet); + result = (statusGot == DBStatus::OK) && result; + } + MST_LOG("GetEntries resultSet result: %d, statusGot: %d", result, statusGot); + if (resultSet != nullptr) { + int quatity = resultSet->GetCount(); + result = (quatity == expectCount) && result; + MST_LOG("ResultSet GetCount result: %d, quatity: %d", result, quatity); + result = (delegate->CloseResultSet(resultSet) == DBStatus::OK) && result; + } + return result; +} + +bool CheckSchemaNotExist(KvStoreNbDelegate *&delegate) +{ + bool result = true; + vector entries; + bool valueBool = true; // boolean true + int valueInt = 10; // int value 10 + int64_t valueLong = 15; // long value 15 + double valueDouble = 10.5; // double value 10.5 + + string fieldStr = "$.field1"; + vector queries1, queries2, queries3; + bool functionChoiceComp1[INTERFACE_QTY_6] = {true, false, false, false, false, false}; + QueryGenerate::Instance().GenerateQueryComp(queries1, functionChoiceComp1, fieldStr, valueBool); + bool functionChoiceComp2[INTERFACE_QTY_6] = {false, true, false, false, false, false}; + QueryGenerate::Instance().GenerateQueryComp(queries2, functionChoiceComp2, fieldStr, valueBool); + bool functionChoiceComp3[INTERFACE_QTY_6] = {false, false, true, true, true, true}; + QueryGenerate::Instance().GenerateQueryComp(queries3, functionChoiceComp3, fieldStr, valueBool); + + bool functionChoiceComp4[INTERFACE_QTY_6] = {true, false, false, false, true, true}; + QueryGenerate::Instance().GenerateQueryComp(queries1, functionChoiceComp4, fieldStr, valueInt); + bool functionChoiceComp5[INTERFACE_QTY_6] = {false, true, true, true, false, false}; + QueryGenerate::Instance().GenerateQueryComp(queries2, functionChoiceComp5, fieldStr, valueInt); + + QueryGenerate::Instance().GenerateQueryComp(queries1, functionChoiceComp4, fieldStr, valueLong); + QueryGenerate::Instance().GenerateQueryComp(queries2, functionChoiceComp5, fieldStr, valueLong); + + QueryGenerate::Instance().GenerateQueryComp(queries1, functionChoiceComp4, fieldStr, valueDouble); + QueryGenerate::Instance().GenerateQueryComp(queries2, functionChoiceComp5, fieldStr, valueDouble); + + vector valuesBool = {true}; + vector valuesInt = {10, 11}; + vector valuesLong = {15, 16}; + vector valuesDouble = {10.5, 11.5}; + + bool functionChoiceIn1[INTERFACE_QTY_2] = {true, false}; + QueryGenerate::Instance().GenerateQueryIn(queries1, functionChoiceIn1, fieldStr, valuesBool); + bool functionChoiceIn2[INTERFACE_QTY_2] = {false, true}; + QueryGenerate::Instance().GenerateQueryIn(queries2, functionChoiceIn2, fieldStr, valuesBool); + + QueryGenerate::Instance().GenerateQueryIn(queries1, functionChoiceIn1, fieldStr, valuesInt); + QueryGenerate::Instance().GenerateQueryIn(queries2, functionChoiceIn2, fieldStr, valuesInt); + + QueryGenerate::Instance().GenerateQueryIn(queries1, functionChoiceIn1, fieldStr, valuesLong); + QueryGenerate::Instance().GenerateQueryIn(queries2, functionChoiceIn2, fieldStr, valuesLong); + + QueryGenerate::Instance().GenerateQueryIn(queries1, functionChoiceIn1, fieldStr, valuesDouble); + QueryGenerate::Instance().GenerateQueryIn(queries2, functionChoiceIn2, fieldStr, valuesDouble); + + for (auto const &it : queries1) { + result = (delegate->GetEntries(it, entries) == NOT_FOUND) && result; + } + for (auto const &it : queries2) { + result = (delegate->GetEntries(it, entries) == OK) && result; + result = (entries.size() == SCHEMA_GOT_COUNT_4) && result; + } + for (auto const &it : queries3) { + result = (delegate->GetEntries(it, entries) == INVALID_QUERY_FORMAT) && result; + } + return result; +} + +/** + * @tc.name: Query 001 + * @tc.desc: check valid schema of String type and construct valid query object and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query001, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\":\"fxy\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"abc\",\"field2\":{\"field3\":\"fxz\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\": null ,\"field2\":{\"field3\":\"fxw\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"TRUE\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_STRING); + + /** + * @tc.steps: step2. test the Query interface of EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and check the result with GetEntries. + * @tc.expected: step2. each interface called ok and GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field1", "bxz"); + vector expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query1, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + Query query5 = Query::Select().GreaterThan("$.field1", "abc"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query5, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query2 = Query::Select().NotEqualTo("$.field1", "bxz"); + expectEntry = {entries[INDEX_FIRST], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query2, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + Query query8 = Query::Select().LessThanOrEqualTo("$.field1", "abc"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query8, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + vector scope = {"bxz", "abc"}; + Query query3 = Query::Select().In("$.field1", scope); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_FIRST], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query3, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + Query query7 = Query::Select().GreaterThanOrEqualTo("$.field1", "abc"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query7, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + Query query4 = Query::Select().NotIn("$.field1", scope); + expectEntry = {entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query4, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + + Query query6 = Query::Select().LessThan("$.field1", "abc"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query6, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + + EXPECT_TRUE(CheckSchemaNotExist(delegate)); + + /** + * @tc.steps: step3. test Fuzzy match interface of Like and NotLike where field3 = "fx?" and check. + * @tc.expected: step3. check the result rightly. + */ + Query query10 = Query::Select().Like("$.field2.field3", "fx_"); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_FIRST], entries[INDEX_SECOND]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query10, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + Query query11 = Query::Select().NotLike("$.field2.field3", "fx_"); + expectEntry = {}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query11, expectEntry, 0, DBStatus::NOT_FOUND)); + + /** + * @tc.steps: step4. test IsNull interface where field = field3. + * @tc.expected: step4. check the result rightly. + */ + Query query12 = Query::Select().IsNull("$.field2.field3"); + expectEntry = {entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query12, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + /** + * @tc.steps: step5. test OrderBy interface where field = field1. + * @tc.expected: step5. check the result rightly. + */ + Query query13 = Query::Select().OrderBy("$.field2.field3"); + expectEntry = {entries[INDEX_THIRD], entries[INDEX_FORTH], entries[INDEX_SECOND], entries[INDEX_ZEROTH], + entries[INDEX_FIRST]}; + EXPECT_TRUE(CheckSchemaQueryForOrderBy(delegate, query13, expectEntry, SCHEMA_GOT_COUNT_5, DBStatus::OK)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +bool InvalidFieldCheck(KvStoreNbDelegate *&delegate, string field, DBStatus status) +{ + bool result = true; + Query query1 = Query::Select().EqualTo(field, "bxz"); + vector expectEntry = {}; + result = (CheckSchemaQuery(delegate, query1, expectEntry, 0, status)) && result; + MST_LOG("EqualTo result: %d", result); + Query query2 = Query::Select().NotEqualTo(field, "bxz"); + result = (CheckSchemaQuery(delegate, query2, expectEntry, 0, status)) && result; + MST_LOG("NotEqualTo result: %d", result); + + vector scope = {"bxz", "abc"}; + Query query3 = Query::Select().In(field, scope); + result = (CheckSchemaQuery(delegate, query3, expectEntry, 0, status)) && result; + MST_LOG("In result: %d", result); + + Query query4 = Query::Select().NotIn(field, scope); + result = (CheckSchemaQuery(delegate, query4, expectEntry, 0, status)) && result; + MST_LOG("NoIn result: %d", result); + + Query query5 = Query::Select().GreaterThan(field, "abc"); + result = (CheckSchemaQuery(delegate, query5, expectEntry, 0, status)) && result; + MST_LOG("GreaterThan result: %d", result); + + Query query6 = Query::Select().LessThan(field, "abc"); + result = (CheckSchemaQuery(delegate, query6, expectEntry, 0, status)) && result; + MST_LOG("LessThan result: %d", result); + + Query query7 = Query::Select().GreaterThanOrEqualTo(field, "abc"); + result = (CheckSchemaQuery(delegate, query7, expectEntry, 0, status)) && result; + MST_LOG("GreaterThanOrEqualTo result: %d", result); + + Query query8 = Query::Select().LessThanOrEqualTo(field, "abc"); + result = (CheckSchemaQuery(delegate, query8, expectEntry, 0, status)) && result; + MST_LOG("LessThanOrEqualTo result: %d", result); + return result; +} + +/** + * @tc.name: Query 002 + * @tc.desc: check valid schema of String type and construct invalid fields and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query002, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\":\"fxy\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"abc\",\"field2\":{\"field3\":\"fxz\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\": null ,\"field2\":{\"field3\":\"fxw\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"TRUE\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_STRING); + + /** + * @tc.steps: step2. use notexist field5 to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo and GetEntries with Query. + * @tc.expected: step2. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field5", DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step3. use not Leaf node field to test EqualTo/NotEqualTo/In/NotIn/ + * GreaterThan/LessThan/GreaterThanOrEqualTo/LessThanOrEqualTo and call GetEntries and check. + * @tc.expected: step3. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field2.field4", DBStatus::INVALID_QUERY_FIELD)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field3.field2", DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step4. use invalid format field to test EqualTo/NotEqualTo/In/NotIn/ + * GreaterThan/LessThan/GreaterThanOrEqualTo/LessThanOrEqualTo and call GetEntries and check. + * @tc.expected: step4. GetEntries return INVALID_QUERY_FORMAT. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, ".field2.field3", DBStatus::INVALID_QUERY_FORMAT)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$$field1", DBStatus::INVALID_QUERY_FORMAT)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +bool CheckSchemaBoolNotExist(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueStringTrue = "true"; + string valueString = "abc"; + int64_t valueLong = 15; // long value 15 + double valueDouble = 10.5; // double value 10.5 + float valueFloat = 5.0; // float value 5.0 + + vector queries1, queries2; + queries1.push_back(Query::Select().EqualTo("$.field1", valueStringTrue)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueString)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueLong)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueDouble)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueFloat)); + + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueStringTrue)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueString)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueLong)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueDouble)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueFloat)); + + vector valuesStringTrue = {"true"}; + vector valuesString = {"abc"}; + vector valuesLong = {15}; + vector valuesDouble = {10.5}; + vector valuesFloat = {5.0}; + + queries1.push_back(Query::Select().In("$.field1", valuesStringTrue)); + queries1.push_back(Query::Select().In("$.field1", valuesString)); + queries1.push_back(Query::Select().In("$.field1", valuesLong)); + queries1.push_back(Query::Select().In("$.field1", valuesDouble)); + queries1.push_back(Query::Select().In("$.field1", valuesFloat)); + + queries2.push_back(Query::Select().NotIn("$.field1", valuesStringTrue)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesString)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesLong)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesDouble)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesFloat)); + + vector entries1; + for (auto const &it : queries1) { + result = (delegate->GetEntries(it, entries1) == NOT_FOUND) && result; + } + vector expectEntry + = {entries[INDEX_ZEROTH], entries[INDEX_FIRST], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + for (auto &it : queries2) { + result = CheckSchemaQuery(delegate, it, expectEntry, SCHEMA_GOT_COUNT_4, DBStatus::OK) && result; + } + return result; +} + +bool CheckSchemaBoolExist(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueStringZero = "0"; + int valueInt = 1; // int value 1 + Query query = Query::Select().EqualTo("$.field1", valueStringZero); + vector expectEntry = {entries[INDEX_FIRST]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)) && result; + query = Query::Select().EqualTo("$.field1", valueInt); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)) && result; + query = Query::Select().NotEqualTo("$.field1", valueStringZero); + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)) && result; + query = Query::Select().NotEqualTo("$.field1", valueInt); + expectEntry = {entries[INDEX_FIRST]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)) && result; + + vector valuesStringZero = {"0"}; + vector valuesInt = {1}; + query = Query::Select().In("$.field1", valuesStringZero); + expectEntry = {entries[INDEX_FIRST]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)) && result; + query = Query::Select().In("$.field1", valuesInt); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)) && result; + + query = Query::Select().NotIn("$.field1", valuesStringZero); + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)) && result; + query = Query::Select().NotIn("$.field1", valuesInt); + expectEntry = {entries[INDEX_FIRST]}; + result = (CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)) && result; + + return result; +} + +/** + * @tc.name: Query 003 + * @tc.desc: check valid schema of Bool type and construct valid query object and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query003, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":false,\"field2\":{\"field3\":false,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null ,\"field2\":{\"field3\":false,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_BOOL); + + /** + * @tc.steps: step2. test the Query interface of EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo and call . + * @tc.expected: step2. each interface called ok and GetEntries return right result. + */ + Query query = Query::Select().EqualTo("$.field1", true); + vector expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + query = Query::Select().NotEqualTo("$.field1", true); + expectEntry = {entries[INDEX_FIRST]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + + vector scope = {true}; + query = Query::Select().In("$.field1", scope); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + query = Query::Select().NotIn("$.field1", scope); + expectEntry = {entries[INDEX_FIRST]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + /** + * @tc.steps: step3. query the illegal value of field and check with the GetEntries. + * @tc.expected: step3. if the value is "0" or 1, can get valid query, or it will return invalid query + * and GetEntries will return NOT_FOUND. + */ + EXPECT_TRUE(CheckSchemaBoolNotExist(delegate, entries)); + EXPECT_TRUE(CheckSchemaBoolExist(delegate, entries)); + + /** + * @tc.steps: step4. test IsNull interface where field = field3. + * @tc.expected: step4. there are entries[0], entries[3], entries[4] in the query. + */ + query = Query::Select().IsNull("$.field2.field3"); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +bool InvalidBoolFieldCheck(KvStoreNbDelegate *&delegate, string field, DBStatus status) +{ + bool result = true; + Query query1 = Query::Select().EqualTo(field, true); + vector expectEntry = {}; + result = (CheckSchemaQuery(delegate, query1, expectEntry, 0, status)) && result; + MST_LOG("Equal To result: %d", result); + + Query query2 = Query::Select().NotEqualTo(field, true); + result = (CheckSchemaQuery(delegate, query2, expectEntry, 0, status)) && result; + MST_LOG("NotEqual To result: %d", result); + + vector scope = {true}; + Query query3 = Query::Select().In(field, scope); + result = (CheckSchemaQuery(delegate, query3, expectEntry, 0, status)) && result; + MST_LOG("In result: %d", result); + + Query query4 = Query::Select().NotIn(field, scope); + result = (CheckSchemaQuery(delegate, query4, expectEntry, 0, status)) && result; + MST_LOG("Not in result: %d", result); + + return result; +} + +/** + * @tc.name: Query 004 + * @tc.desc: check valid schema of Bool type and construct invalid field and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query004, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":false,\"field2\":{\"field3\":false,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null ,\"field2\":{\"field3\":false,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":true,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_BOOL); + + /** + * @tc.steps: step2. use notexist field5 to test EqualTo/NotEqualTo/In/NotIn and call GetEntries with Query. + * @tc.expected: step2. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidBoolFieldCheck(delegate, "$.field5", DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step3. use not Leaf node field to test EqualTo/NotEqualTo/In/NotIn and call GetEntries to check. + * @tc.expected: step3. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidBoolFieldCheck(delegate, "$.field2.field4", DBStatus::INVALID_QUERY_FIELD)); + EXPECT_TRUE(InvalidBoolFieldCheck(delegate, "$.field3.field2", DBStatus::INVALID_QUERY_FIELD)); + /** + * @tc.steps: step4. use invalid format field to test EqualTo/NotEqualTo/In/NotIn and call GetEntries with Query. + * @tc.expected: step4. GetEntries return INVALID_QUERY_FORMAT. + */ + EXPECT_TRUE(InvalidBoolFieldCheck(delegate, ".field2.field3", DBStatus::INVALID_QUERY_FORMAT)); + EXPECT_TRUE(InvalidBoolFieldCheck(delegate, "$$field1", DBStatus::INVALID_QUERY_FORMAT)); + + /** + * @tc.steps: step5. use invalid format field to test + * GreaterThan/LessThan/GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step5. GetEntries return INVALID_QUERY_FORMAT. + */ + Query query = Query::Select().GreaterThan("$.field1", "1"); + vector expectEntry = {}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, 0, DBStatus::INVALID_QUERY_FORMAT)); + query = Query::Select().GreaterThanOrEqualTo("$.field1", "1"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, 0, DBStatus::INVALID_QUERY_FORMAT)); + query = Query::Select().LessThan("$.field1", "1"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, 0, DBStatus::INVALID_QUERY_FORMAT)); + query = Query::Select().LessThanOrEqualTo("$.field1", "1"); + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, 0, DBStatus::INVALID_QUERY_FORMAT)); + query = Query::Select().OrderBy("$.field1", true); + EXPECT_TRUE(CheckSchemaQuery(delegate, query, expectEntry, 0, DBStatus::INVALID_QUERY_FORMAT)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +bool CheckSchemaExceptionValue(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueStringTen = "10"; // string 10 + string valueString = "abc"; + float valueFloat = 10.0; // float 10.0 + bool valueBool = false; // boolean false + vector queries1, queries2, queries3; + queries1.push_back(Query::Select().EqualTo("$.field1", valueString)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueBool)); + + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueString)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueBool)); + + vector valuesString = {"abc"}; + vector valuesBool = {false}; + vector valuesDouble = {10.5}; + queries1.push_back(Query::Select().In("$.field1", valuesString)); + queries1.push_back(Query::Select().In("$.field1", valuesBool)); + queries1.push_back(Query::Select().In("$.field1", valuesDouble)); + + queries2.push_back(Query::Select().NotIn("$.field1", valuesString)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesBool)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesDouble)); + + queries1.push_back(Query::Select().GreaterThan("$.field1", valueString)); + queries3.push_back(Query::Select().GreaterThan("$.field1", valueBool)); + + queries2.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueStringTen)); + queries2.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueFloat)); + queries1.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueString)); + queries3.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueBool)); + + vector expectEntry3 = {}; + Query query = Query::Select().LessThan("$.field1", valueStringTen); + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::NOT_FOUND) && result; + query = Query::Select().LessThan("$.field1", valueFloat); + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::NOT_FOUND) && result; + + queries2.push_back(Query::Select().LessThan("$.field1", valueString)); + queries3.push_back(Query::Select().LessThan("$.field1", valueBool)); + + queries2.push_back(Query::Select().LessThanOrEqualTo("$.field1", valueString)); + queries3.push_back(Query::Select().LessThanOrEqualTo("$.field1", valueBool)); + + vector entries1; + for (auto const &it : queries1) { + result = (delegate->GetEntries(it, entries1) == NOT_FOUND) && result; + } + vector expectEntry2 = + {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + for (auto &it : queries2) { + result = CheckSchemaQuery(delegate, it, expectEntry2, SCHEMA_GOT_COUNT_4, DBStatus::OK) && result; + } + for (auto &it : queries3) { + result = CheckSchemaQuery(delegate, it, expectEntry3, 0, DBStatus::INVALID_QUERY_FORMAT) && result; + } + return result; +} + +bool CheckSchemaIntValue(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueStringTen = "10"; // string 10 + float valueFloat = 10.0; // float 10.0 + double valueDouble = 10.5; // double value 10.5 + Query query = Query::Select().EqualTo("$.field1", valueStringTen); + vector expectEntry1 = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().EqualTo("$.field1", valueFloat); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().EqualTo("$.field1", valueDouble); + vector entries1; + result = (delegate->GetEntries(query, entries1) == NOT_FOUND) && result; + + query = Query::Select().NotEqualTo("$.field1", valueStringTen); + vector expectEntry2 = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().NotEqualTo("$.field1", valueFloat); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().GreaterThan("$.field1", valueStringTen); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().GreaterThan("$.field1", valueFloat); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().LessThanOrEqualTo("$.field1", valueStringTen); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().LessThanOrEqualTo("$.field1", valueFloat); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + vector valuesStringTen = {"10"}; + vector valuesFloat = {10.0}; + query = Query::Select().In("$.field1", valuesStringTen); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().In("$.field1", valuesFloat); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().NotIn("$.field1", valuesStringTen); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().NotIn("$.field1", valuesFloat); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().GreaterThan("$.field1", valueDouble); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().GreaterThanOrEqualTo("$.field1", valueDouble); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().LessThan("$.field1", valueDouble); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().LessThanOrEqualTo("$.field1", valueDouble); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + + query = Query::Select().NotEqualTo("$.field1", valueDouble); + expectEntry2 = {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_4, DBStatus::OK) && result; + + return result; +} + +/** + * @tc.name: Query 005 + * @tc.desc: check valid schema of Int type and construct valid query object and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query005, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":15,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":10,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":20,\"field2\":{\"field3\":null,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_INT); + + /** + * @tc.steps: step2. test the Query interface of EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo and check the return query with GetEntries. + * @tc.expected: step2. each interface called ok and GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field1", 10); + vector expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query1, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query2 = Query::Select().NotEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query2, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + vector scope = {10}; + Query query3 = Query::Select().In("$.field1", scope); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query3, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query4 = Query::Select().NotIn("$.field1", scope); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query4, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query5 = Query::Select().GreaterThan("$.field1", 10); + EXPECT_TRUE(CheckSchemaQuery(delegate, query5, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query6 = Query::Select().LessThan("$.field1", 10); + expectEntry = {}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query6, expectEntry, 0, DBStatus::NOT_FOUND)); + + Query query7 = Query::Select().GreaterThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query7, expectEntry, SCHEMA_GOT_COUNT_4, DBStatus::OK)); + + Query query8 = Query::Select().LessThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query8, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + /** + * @tc.steps: step3. query the illegal value of field and check with the GetEntries. + * @tc.expected: step3. if the value is "0" or 1, can get valid query, or it will return invalid query + * and GetEntries will return NOT_FOUND. + */ + EXPECT_TRUE(CheckSchemaExceptionValue(delegate, entries)); + EXPECT_TRUE(CheckSchemaIntValue(delegate, entries)); + + /** + * @tc.steps: step4. test IsNull interface where field = field3. + * @tc.expected: step4. create and put successfully. + */ + Query query9 = Query::Select().IsNull("$.field2.field3"); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query9, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +template +bool InvalidFieldCheck(KvStoreNbDelegate *&delegate, string field, T value, DBStatus status) +{ + bool result = true; + Query query1 = Query::Select().EqualTo(field, value); + vector expectEntry = {}; + result = (CheckSchemaQuery(delegate, query1, expectEntry, 0, status)) && result; + MST_LOG("Equal to result: %d", result); + + Query query2 = Query::Select().NotEqualTo(field, value); + result = (CheckSchemaQuery(delegate, query2, expectEntry, 0, status)) && result; + MST_LOG("NotEqual to result: %d", result); + + vector scope = {value}; + Query query3 = Query::Select().In(field, scope); + result = (CheckSchemaQuery(delegate, query3, expectEntry, 0, status)) && result; + MST_LOG("In result: %d", result); + + Query query4 = Query::Select().NotIn(field, scope); + result = (CheckSchemaQuery(delegate, query4, expectEntry, 0, status)) && result; + MST_LOG("NotIn result: %d", result); + + Query query5 = Query::Select().GreaterThan(field, value); + result = (CheckSchemaQuery(delegate, query5, expectEntry, 0, status)) && result; + MST_LOG("GreaterThan result: %d", result); + + Query query6 = Query::Select().LessThan(field, value); + result = (CheckSchemaQuery(delegate, query6, expectEntry, 0, status)) && result; + MST_LOG("LessThan result: %d", result); + + Query query7 = Query::Select().GreaterThanOrEqualTo(field, value); + result = (CheckSchemaQuery(delegate, query7, expectEntry, 0, status)) && result; + MST_LOG("GreaterThanOrEqualTo result: %d", result); + + Query query8 = Query::Select().LessThanOrEqualTo(field, value); + result = (CheckSchemaQuery(delegate, query8, expectEntry, 0, status)) && result; + MST_LOG("LessThanOrEqualTo result: %d", result); + + Query query9 = Query::Select().OrderBy(field); + result = (CheckSchemaQueryForOrderBy(delegate, query9, expectEntry, 0, status)) && result; + MST_LOG("OrderBy result: %d", result); + return result; +} + +/** + * @tc.name: Query 006 + * @tc.desc: check valid schema of Int type and construct invalid field and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query006, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":15 ,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":10 ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":20,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_INT); + + /** + * @tc.steps: step2. use notexist field5 to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step2. GetEntries return INVALID_QUERY_FIELD. + */ + int valueIntTen = 10; + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field5", valueIntTen, DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step3. use not Leaf node field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries to check. + * @tc.expected: step3. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field2.field4", valueIntTen, DBStatus::INVALID_QUERY_FIELD)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field3.field2", valueIntTen, DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step4. use invalid format field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step4. GetEntries return INVALID_QUERY_FORMAT. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, ".field2.field3", valueIntTen, DBStatus::INVALID_QUERY_FORMAT)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$$field1", valueIntTen, DBStatus::INVALID_QUERY_FORMAT)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 007 + * @tc.desc: check valid schema of Long type and construct valid query object and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query007, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":18 ,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":-25 ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":20,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_LONG); + + /** + * @tc.steps: step2. test the Query interface of EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo and check the return query with GetEntries. + * @tc.expected: step2. each interface called ok and GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field1", 10); + vector expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query1, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query2 = Query::Select().NotEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query2, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + vector scope = {10, -10}; + Query query3 = Query::Select().In("$.field1", scope); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query3, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query4 = Query::Select().NotIn("$.field1", scope); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query4, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query5 = Query::Select().GreaterThan("$.field1", 10); + EXPECT_TRUE(CheckSchemaQuery(delegate, query5, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query6 = Query::Select().LessThan("$.field1", 10); + expectEntry = {}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query6, expectEntry, 0, DBStatus::NOT_FOUND)); + + Query query7 = Query::Select().GreaterThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query7, expectEntry, SCHEMA_GOT_COUNT_4, DBStatus::OK)); + + Query query8 = Query::Select().LessThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query8, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + /** + * @tc.steps: step3. query the exception value of field and check with the GetEntries. + * @tc.expected: step3. if the value is "10" or 10.0, can get valid query, or it will return invalid query + * and GetEntries will return NOT_FOUND. + */ + EXPECT_TRUE(CheckSchemaExceptionValue(delegate, entries)); + EXPECT_TRUE(CheckSchemaIntValue(delegate, entries)); + + /** + * @tc.steps: step4. test IsNull interface where field = field3. + * @tc.expected: step4. create and put successfully. + */ + Query query9 = Query::Select().IsNull("$.field2.field3"); + expectEntry = {entries[INDEX_ZEROTH], entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query9, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 008 + * @tc.desc: check valid schema of Long type and construct invalid field and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query008, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":18 ,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10,\"field2\":{\"field3\":-25 ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":20,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_LONG); + + /** + * @tc.steps: step2. use notexist field5 to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step2. GetEntries return INVALID_QUERY_FIELD. + */ + int64_t valueLongTen = 10; + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field5", valueLongTen, DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step3. use not Leaf node field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries to check. + * @tc.expected: step3. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field2.field4", valueLongTen, DBStatus::INVALID_QUERY_FIELD)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field3.field2", valueLongTen, DBStatus::INVALID_QUERY_FIELD)); + /** + * @tc.steps: step4. use invalid format field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step4. GetEntries return INVALID_QUERY_FORMAT. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, ".field2.field3", valueLongTen, DBStatus::INVALID_QUERY_FORMAT)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$$field1", valueLongTen, DBStatus::INVALID_QUERY_FORMAT)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +bool CheckSchemaDoubleExceptionValue(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueString = "abc"; + int64_t valueLong = 15; // long value 15 + vector queries1, queries2; + queries1.push_back(Query::Select().EqualTo("$.field1", valueString)); + queries1.push_back(Query::Select().EqualTo("$.field1", valueLong)); + + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueString)); + queries2.push_back(Query::Select().NotEqualTo("$.field1", valueLong)); + + vector valuesStringZeroPoint = {"0.0"}; + vector valuesBool = {false}; + vector valuesString = {"abc"}; + vector valuesLong = {15}; + Query query = Query::Select().In("$.field1", valuesStringZeroPoint); + vector expectEntry1 = {entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_1, DBStatus::OK) && result; + query = Query::Select().In("$.field1", valuesBool); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_1, DBStatus::OK) && result; + queries1.push_back(Query::Select().In("$.field1", valuesString)); + queries1.push_back(Query::Select().In("$.field1", valuesLong)); + + query = Query::Select().NotIn("$.field1", valuesStringZeroPoint); + vector expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_SECOND], entries[INDEX_THIRD]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_3, DBStatus::OK) && result; + query = Query::Select().NotIn("$.field1", valuesBool); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_3, DBStatus::OK) && result; + queries2.push_back(Query::Select().NotIn("$.field1", valuesString)); + queries2.push_back(Query::Select().NotIn("$.field1", valuesLong)); + + queries1.push_back(Query::Select().GreaterThan("$.field1", valueString)); + queries1.push_back(Query::Select().GreaterThan("$.field1", valueLong)); + + queries1.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueString)); + queries1.push_back(Query::Select().GreaterThanOrEqualTo("$.field1", valueLong)); + + queries2.push_back(Query::Select().LessThan("$.field1", valueString)); + queries2.push_back(Query::Select().LessThan("$.field1", valueLong)); + + queries2.push_back(Query::Select().LessThanOrEqualTo("$.field1", valueString)); + queries2.push_back(Query::Select().LessThanOrEqualTo("$.field1", valueLong)); + + vector entries1; + for (auto const &it : queries1) { + result = (delegate->GetEntries(it, entries1) == NOT_FOUND) && result; + } + expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + for (auto &it : queries2) { + result = CheckSchemaQuery(delegate, it, expectEntry2, SCHEMA_GOT_COUNT_4, DBStatus::OK) && result; + } + return result; +} + +bool CheckSchemaDoubleValue(KvStoreNbDelegate *&delegate, vector &entries) +{ + bool result = true; + string valueStringZeroPoint = "0.0"; // string 0.0 + bool valueBool = false; // boolean false + Query query = Query::Select().EqualTo("$.field1", valueStringZeroPoint); + vector expectEntry1 = {entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_1, DBStatus::OK) && result; + query = Query::Select().EqualTo("$.field1", valueBool); + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_1, DBStatus::OK) && result; + + query = Query::Select().NotEqualTo("$.field1", valueStringZeroPoint); + vector expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_SECOND], entries[INDEX_THIRD]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_3, DBStatus::OK) && result; + query = Query::Select().NotEqualTo("$.field1", valueBool); + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_3, DBStatus::OK) && result; + + query = Query::Select().GreaterThan("$.field1", valueStringZeroPoint); + vector expectEntry3 = {entries[INDEX_FIRST], entries[INDEX_THIRD]}; + result = CheckSchemaQuery(delegate, query, expectEntry3, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().GreaterThan("$.field1", valueBool); + expectEntry3 = {}; + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::INVALID_QUERY_FORMAT) && result; + + query = Query::Select().GreaterThanOrEqualTo("$.field1", valueStringZeroPoint); + expectEntry2 = {entries[INDEX_FIRST], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_3, DBStatus::OK) && result; + query = Query::Select().GreaterThanOrEqualTo("$.field1", valueBool); + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::INVALID_QUERY_FORMAT) && result; + + query = Query::Select().LessThan("$.field1", valueStringZeroPoint); + expectEntry2 = {entries[INDEX_SECOND]}; + result = CheckSchemaQuery(delegate, query, expectEntry2, SCHEMA_GOT_COUNT_1, DBStatus::OK) && result; + query = Query::Select().LessThan("$.field1", valueBool); + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::INVALID_QUERY_FORMAT) && result; + + query = Query::Select().LessThanOrEqualTo("$.field1", valueStringZeroPoint); + expectEntry1 = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + result = CheckSchemaQuery(delegate, query, expectEntry1, SCHEMA_GOT_COUNT_2, DBStatus::OK) && result; + query = Query::Select().LessThanOrEqualTo("$.field1", valueBool); + result = CheckSchemaQuery(delegate, query, expectEntry3, 0, DBStatus::INVALID_QUERY_FORMAT) && result; + + return result; +} +/** + * @tc.name: Query 009 + * @tc.desc: check valid schema of Double type and construct valid query object and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query009, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10.0,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10.0,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":-10.0 ,\"field2\":{\"field3\":30,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10.5,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":-0.0,\"field2\":{\"field3\":12.5 ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_DOUBLE); + + /** + * @tc.steps: step2. test the Query interface of EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo and check the return query with GetEntries. + * @tc.expected: step2. each interface called ok and GetEntries return right result. + */ + Query query1 = Query::Select().EqualTo("$.field1", 10); + vector expectEntry = {entries[INDEX_FIRST]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query1, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + + Query query2 = Query::Select().NotEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query2, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + + vector scope = {10, -10}; + Query query3 = Query::Select().In("$.field1", scope); + expectEntry = {entries[INDEX_FIRST], entries[INDEX_SECOND]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query3, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query4 = Query::Select().NotIn("$.field1", scope); + expectEntry = {entries[INDEX_THIRD], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query4, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query5 = Query::Select().GreaterThan("$.field1", 10); + expectEntry = {entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query5, expectEntry, SCHEMA_GOT_COUNT_1, DBStatus::OK)); + + Query query6 = Query::Select().LessThan("$.field1", 10); + expectEntry = {entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query6, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query7 = Query::Select().GreaterThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_FIRST], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query7, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + Query query8 = Query::Select().LessThanOrEqualTo("$.field1", 10); + expectEntry = {entries[INDEX_FIRST], entries[INDEX_SECOND], entries[INDEX_FORTH]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query8, expectEntry, SCHEMA_GOT_COUNT_3, DBStatus::OK)); + /** + * @tc.steps: step3. query the exception value of field and check with the GetEntries. + * @tc.expected: step3. if the value is "0.0" or false, can get valid query, or it will return invalid query + * and GetEntries will return NOT_FOUND. + */ + EXPECT_TRUE(CheckSchemaDoubleExceptionValue(delegate, entries)); + EXPECT_TRUE(CheckSchemaDoubleValue(delegate, entries)); + + /** + * @tc.steps: step4. test IsNull interface where field = field3. + * @tc.expected: step4. create and put successfully. + */ + Query query9 = Query::Select().IsNull("$.field2.field3"); + expectEntry = {entries[INDEX_FIRST], entries[INDEX_THIRD]}; + EXPECT_TRUE(CheckSchemaQuery(delegate, query9, expectEntry, SCHEMA_GOT_COUNT_2, DBStatus::OK)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 010 + * @tc.desc: check valid schema of Double type and construct invalid field and traverse all function of Query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query010, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + + schemasValue.push_back("{\"field1\":null,\"field2\":{\"field3\":10.0,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10.0,\"field2\":{\"field3\":null,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":-10.0 ,\"field2\":{\"field3\":30,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":10.5,\"field2\":{\"field3\":null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":-0.0,\"field2\":{\"field3\":12.5 ,\"field4\":[]}}"); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_DOUBLE); + + /** + * @tc.steps: step2. use notexist field5 to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step2. GetEntries return INVALID_QUERY_FIELD. + */ + double valueDoubleTen = 10.0; + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field5", valueDoubleTen, DBStatus::INVALID_QUERY_FIELD)); + + /** + * @tc.steps: step3. use not Leaf node field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries to check. + * @tc.expected: step3. GetEntries return INVALID_QUERY_FIELD. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field2.field4", valueDoubleTen, DBStatus::INVALID_QUERY_FIELD)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$.field3.field2", valueDoubleTen, DBStatus::INVALID_QUERY_FIELD)); + /** + * @tc.steps: step4. use invalid format field to test EqualTo/NotEqualTo/In/NotIn/GreaterThan/LessThan/ + * GreaterThanOrEqualTo/LessThanOrEqualTo/OrderBy and call GetEntries with Query. + * @tc.expected: step4. GetEntries return INVALID_QUERY_FORMAT. + */ + EXPECT_TRUE(InvalidFieldCheck(delegate, ".field2.field3", valueDoubleTen, DBStatus::INVALID_QUERY_FORMAT)); + EXPECT_TRUE(InvalidFieldCheck(delegate, "$$field1", valueDoubleTen, DBStatus::INVALID_QUERY_FORMAT)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +vector GenerateCombinationSchemaValue() +{ + vector schemasValue; + schemasValue.push_back("{\"field1\":\"abc\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":9," + "\"field6\":{\"field7\":1000,\"field8\":12}}}}"); // value1 + + schemasValue.push_back("{\"field1\":\"ab123\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":88," + "\"field6\":{\"field7\":-100,\"field8\":-99}}}}"); // value2 + + schemasValue.push_back("{\"field1\":\"abfxy\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":10," + "\"field6\":{\"field7\":0,\"field8\":38}}}}"); // value3 + + schemasValue.push_back("{\"field1\":\"ab789\",\"field2\":{\"field3\":false,\"field4\":{\"field5\":999," + "\"field6\":{\"field7\":50,\"field8\":15.8}}}}"); // value4 + + schemasValue.push_back("{\"field1\":\"ab000\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":33," + "\"field6\":{\"field7\":30,\"field8\":149}}}}"); // value5 + + schemasValue.push_back("{\"field1\":\"abxxx\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":12," + "\"field6\":{\"field7\":120,\"field8\":-79}}}}"); // value6 + + schemasValue.push_back("{\"field1\":\"ab\",\"field2\":{\"field3\":true,\"field4\":{\"field5\":20," + "\"field6\":{\"field7\":82,\"field8\":150.999}}}}"); // value7 + + return schemasValue; +} + +/** + * @tc.name: Query 011 + * @tc.desc: verify that GetCount interface can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query011, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create common db and close it, and then open it with schema mode and put some schema data to + * the new mode db; + * @tc.expected: step1. operate successfully. + */ + Option option = g_option; + option.isMemoryDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of LessThan on field5 = 100, + * and NotEqualTo on field7 = -100 and NotLike on field1 = "%c". + * @tc.expected: step2. construct successfully. + */ + Query query1 = Query::Select().LessThan("$.field2.field4.field5", 100).And(). + NotEqualTo("$.field2.field4.field6.field7", -100).And().NotLike("$.field1", "%c"); + + /** + * @tc.steps: step3. use GetCount interface to check the count of records in the query object. + * @tc.expected: step3. the count is 4. + */ + int count = 0; + EXPECT_TRUE(delegate->GetCount(query1, count) == DBStatus::OK); + EXPECT_EQ(count, SCHEMA_GOT_COUNT_4); + + /** + * @tc.steps: step4. construct query object that test combination interface of LessThan on field5 = 100, + * and NotEqualTo on field7 = -100 or NotLike on field1 = "%c". + * @tc.expected: step4. construct successfully. + */ + Query query2 = Query::Select().LessThan("$.field2.field4.field5", 100).And(). + NotEqualTo("$.field2.field4.field6.field7", -100).Or().NotLike("$.field1", "%c"); + + /** + * @tc.steps: step5. use GetCount interface to check the count of records in the query object. + * @tc.expected: step5. the count is 7. + */ + EXPECT_TRUE(delegate->GetCount(query2, count) == DBStatus::OK); + EXPECT_EQ(count, (SCHEMA_GOT_COUNT_4 + SCHEMA_GOT_COUNT_3)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 012 + * @tc.desc: verify that OrderBy and Limit interface and the offset of limit is smaller than the count of the records + * got can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query012, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of GreaterThanOrEqualTo on + * field7 = 0, and EqualTo on field3 = true and then OrderBy on field5 by asec when get 3 records begin from 1. + * @tc.expected: step2. construct successfully. + */ + Query query = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field6.field7", 0).And(). + EqualTo("$.field2.field3", true).OrderBy("$.field2.field4.field5", false).Limit(3, 1); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query object. + * @tc.expected: step3. the count is 4. + */ + KvStoreResultSet *resultSet = nullptr; + Entry entry; + EXPECT_EQ(delegate->GetEntries(query, resultSet), DBStatus::OK); + EXPECT_EQ(resultSet->GetCount(), SCHEMA_GOT_COUNT_3); + EXPECT_EQ(resultSet->GetPosition(), CURSOR_POSITION_NEGATIVE1); + EXPECT_EQ(resultSet->MoveToPrevious(), false); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SIXTH].key); + EXPECT_EQ(entry.value, entries[INDEX_SIXTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FIFTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FIFTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SECOND].key); + EXPECT_EQ(entry.value, entries[INDEX_SECOND].value); + EXPECT_EQ(resultSet->IsLast(), true); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + + /** + * @tc.steps: step4. use GetEntries interface to check the records in the query object. + * @tc.expected: step4. the result and the order in the vector is: entries[6], entries[5], entries[2]. + */ + vector entriesGot, entriesExpect; + EXPECT_TRUE(delegate->GetEntries(query, entriesGot) == DBStatus::OK); + entriesExpect = {entries[INDEX_SIXTH], entries[INDEX_FIFTH], entries[INDEX_SECOND]}; + EXPECT_TRUE(CompareEntriesVector(entriesGot, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 013 + * @tc.desc: verify that OrderBy and Limit interface but the offset of limit is greater than the count of the records + * got can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query013, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of GreaterThanOrEqualTo on + * field7 = 0, and EqualTo on field3 = true and then OrderBy on field5 by asec when get 5 records begin from 1. + * @tc.expected: step2. construct successfully. + */ + Query query = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field6.field7", 0).And(). + EqualTo("$.field2.field3", true).OrderBy("$.field2.field4.field5", false).Limit(5, 1); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query object. + * @tc.expected: step3. the count is 4. + */ + KvStoreResultSet *resultSet = nullptr; + Entry entry; + EXPECT_EQ(delegate->GetEntries(query, resultSet), DBStatus::OK); + EXPECT_EQ(resultSet->GetCount(), FOUR_RECORDS); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SIXTH].key); + EXPECT_EQ(entry.value, entries[INDEX_SIXTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FIFTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FIFTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SECOND].key); + EXPECT_EQ(entry.value, entries[INDEX_SECOND].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_ZEROTH].key); + EXPECT_EQ(entry.value, entries[INDEX_ZEROTH].value); + EXPECT_EQ(resultSet->IsLast(), true); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + + /** + * @tc.steps: step4. use GetEntries interface to check the records in the query object. + * @tc.expected: step4. the result and the order in the vector is: entries[6], entries[5], entries[2], entries[0]. + */ + vector entriesGot, entriesExpect; + EXPECT_TRUE(delegate->GetEntries(query, entriesGot) == DBStatus::OK); + entriesExpect = {entries[INDEX_SIXTH], entries[INDEX_FIFTH], entries[INDEX_SECOND], entries[INDEX_ZEROTH]}; + EXPECT_TRUE(CompareEntriesVector(entriesGot, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 014 + * @tc.desc: verify that OrderBy and Limit interface but the offset of limit is nagative + * can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query014, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of LessThanOrEqualTo on field7 = 1000, + * and EqualTo on field3 = true and Like field1 = ab*, and then OrderBy on field8 by asec + * when get 10 records begin from -1. + * @tc.expected: step2. construct successfully. + */ + Query query = Query::Select().LessThanOrEqualTo("$.field2.field4.field6.field7", 1000).Or(). + EqualTo("$.field2.field3", true).And().Like("$.field1", "ab%"). + OrderBy("$.field2.field4.field6.field8", true).Limit(10, -1); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query object. + * @tc.expected: step3. the count is 6. + */ + KvStoreResultSet *resultSet = nullptr; + Entry entry; + EXPECT_EQ(delegate->GetEntries(query, resultSet), DBStatus::OK); + EXPECT_EQ(resultSet->GetCount(), 7); // 7 records + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FIRST].key); + EXPECT_EQ(entry.value, entries[INDEX_FIRST].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FIFTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FIFTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_ZEROTH].key); + EXPECT_EQ(entry.value, entries[INDEX_ZEROTH].value); + EXPECT_TRUE(resultSet->Move(CURSOR_OFFSET_2)); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SECOND].key); + EXPECT_EQ(entry.value, entries[INDEX_SECOND].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FORTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FORTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_SIXTH].key); + EXPECT_EQ(entry.value, entries[INDEX_SIXTH].value); + EXPECT_EQ(resultSet->IsLast(), true); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + + /** + * @tc.steps: step4. use GetEntries interface to check the records in the query object. + * @tc.expected: step4. the result and the order in the vector is: entries[1], entries[5], + * entries[0], entries[2], entries[4], entries[6]. + */ + vector entriesGot, entriesExpect; + EXPECT_TRUE(delegate->GetEntries(query, entriesGot) == OK); + entriesExpect = {entries[INDEX_FIRST], entries[INDEX_FIFTH], entries[INDEX_ZEROTH], entries[INDEX_THIRD], + entries[INDEX_SECOND], entries[INDEX_FORTH], entries[INDEX_SIXTH]}; + EXPECT_TRUE(CompareEntriesVector(entriesGot, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 015 + * @tc.desc: verify that OrderBy and Limit interface but the offset of limit is out of the range of the query result + * can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query015, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of GreaterThanOrEqualTo on + * field5 = 10, and NotEqualTo on field3 = true and then OrderBy on field5 by asec + * when get 10 records begin from 6. + * @tc.expected: step2. construct successfully. + */ + vector doubleRange = {150.999}; + Query query = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().EqualTo("$.field2.field3", + true).And().NotIn("$.field2.field4.field6.field8", doubleRange).Limit(10, 6); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query object. + * @tc.expected: step3. the count is 0. + */ + KvStoreResultSet *resultSet = nullptr; + Entry entry; + EXPECT_EQ(delegate->GetEntries(query, resultSet), DBStatus::OK); + EXPECT_EQ(resultSet->GetCount(), NO_RECORD); + EXPECT_EQ(resultSet->GetPosition(), CURSOR_POSITION_NEGATIVE1); + EXPECT_EQ(resultSet->IsLast(), false); + EXPECT_EQ(resultSet->IsAfterLast(), true); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + + /** + * @tc.steps: step4. use GetEntries interface to check the records in the query object. + * @tc.expected: step4. no records in the query. + */ + vector entriesGot; + EXPECT_TRUE(delegate->GetEntries(query, entriesGot) == NOT_FOUND); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 016 + * @tc.desc: verify that OrderBy and Limit interface but the limit is not only greater than the result of the + * query result, but also the offset of the limit is out of the range of the query result + * can return right value of the valid query object. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query016, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct query object that test combination interface of Like on field1 = ab*, + * GeaterThan on field5 = 10, and EqualTo on field3 = true and when field in {0, 30, 50, 120, 1000}, + * then OrderBy on field8 by asec when get 10 records begin from -2. + * @tc.expected: step2. construct successfully. + */ + vector longRange = {0, 30, 50, 120, 1000}; + Query query = Query::Select().Like("$.field1", "abc").Or().GreaterThan("$.field2.field4.field5", 10).And(). + EqualTo("$.field2.field3", true).And().In("$.field2.field4.field6.field7", longRange). + OrderBy("$.field2.field4.field6.field8", true).Limit(10, -2); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query object. + * @tc.expected: step3. the count is 3. + */ + KvStoreResultSet *resultSet = nullptr; + Entry entry; + EXPECT_EQ(delegate->GetEntries(query, resultSet), DBStatus::OK); + EXPECT_EQ(resultSet->GetCount(), SCHEMA_GOT_COUNT_3); + EXPECT_EQ(resultSet->GetPosition(), CURSOR_POSITION_NEGATIVE1); + EXPECT_EQ(resultSet->GetEntry(entry), NOT_FOUND); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FIFTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FIFTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_ZEROTH].key); + EXPECT_EQ(entry.value, entries[INDEX_ZEROTH].value); + EXPECT_EQ(resultSet->MoveToNext(), true); + EXPECT_EQ(resultSet->GetEntry(entry), OK); + EXPECT_EQ(entry.key, entries[INDEX_FORTH].key); + EXPECT_EQ(entry.value, entries[INDEX_FORTH].value); + EXPECT_EQ(resultSet->IsLast(), true); + EXPECT_EQ(resultSet->MoveToNext(), false); + EXPECT_EQ(resultSet->IsAfterLast(), true); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + + /** + * @tc.steps: step4. use GetEntries interface to check the records in the query object. + * @tc.expected: step4. the result and the order in the vector is: entries[1], entries[5], + * entries[0], entries[2], entries[4], entries[6]. + */ + vector entriesGot, entriesExpect; + EXPECT_TRUE(delegate->GetEntries(query, entriesGot) == OK); + entriesExpect = {entries[INDEX_FIFTH], entries[INDEX_ZEROTH], entries[INDEX_FORTH]}; + EXPECT_TRUE(CompareEntriesVector(entriesGot, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 017 + * @tc.desc: verify that common db can not support schema query. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query017, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + Option option = g_option; + option.isMemoryDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + vector keys = {KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7}; + for (unsigned long index = 0; index < schemasValue.size(); index++) { + Value value(schemasValue[index].begin(), schemasValue[index].end()); + entries.push_back({keys[index], value}); + } + + for (unsigned long index = 0; index < schemasValue.size(); index++) { + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, entries[index].key, entries[index].value), OK); + } + + /** + * @tc.steps: step2. construct query1 object that test combination interface of Like on field1 = ab*, + * GeaterThan on field5 = 10, and EqualTo on field3 = true and when field in {0, 30, 50, 120, 1000}, + * then OrderBy on field8 by asec when get 10 records begin from -2, + * construct query2 object the same as query1 but has not OrderBy and Limit. + * @tc.expected: step2. construct successfully. + */ + vector longRange = {0, 30, 50, 120, 1000}; + Query query1 = Query::Select().Like("$.field1", "ab*").Or().GreaterThan("$.field2.field4.field5", 10).And(). + EqualTo("$.field2.field3", true).And().In("$.field2.field4.field6.field7", longRange). + OrderBy("$.field2.field4.field6.field8", true).Limit(10, -2); + Query query2 = Query::Select().Like("$.field1", "ab*").Or().GreaterThan("$.field2.field4.field5", 10).And(). + EqualTo("$.field2.field3", true).And().In("$.field2.field4.field6.field7", longRange); + + /** + * @tc.steps: step3. use GetEntries interface to get the resultSet in the query1 and query2 objects. + * @tc.expected: step3. both of them returns NOT_SUPPORT. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(delegate->GetEntries(query1, resultSet), DBStatus::NOT_SUPPORT); + EXPECT_EQ(delegate->GetEntries(query2, resultSet), DBStatus::NOT_SUPPORT); + + /** + * @tc.steps: step4. use GetEntries interface to get the records in the query1 and query2 objects. + * @tc.expected: step4. both of them returns NOT_SUPPORT. + */ + vector entriesGot; + EXPECT_TRUE(delegate->GetEntries(query1, entriesGot) == DBStatus::NOT_SUPPORT); + EXPECT_TRUE(delegate->GetEntries(query2, entriesGot) == DBStatus::NOT_SUPPORT); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 018 + * @tc.desc: verify that constructor illegal query object and returns INVALID_QUERY_FORMAT + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query018, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct illegal query object such as: + * query1: which didn't combine with And() or Or(). + * query2: OrderBy is before other condition. + * query3: Limit is before other condition. + * query4: OrderBy is after Limit. + * @tc.expected: step2. construct successfully. + */ + // LessThan on where field8 = 100 + Query query1 = Query::Select().LessThan("$.field2.field4.field6.field8", 100).NotLike("$.field1", "%c"); + Query query2 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field6.field7", 0). + OrderBy("$.field2.field4.field5", true).And().EqualTo("$.field2.field3", true); + vector doubleRange = {150.999}; + // field5 = 10, get 10 records begin from 6th. + Query query3 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field5", 10).And().EqualTo("$.field2.field3", + true).Limit(10, 6).And().NotIn("$.field2.field4.field6.field8", doubleRange); + // field7 = 0, get 3 records begin from 1 + Query query4 = Query::Select().GreaterThanOrEqualTo("$.field2.field4.field6.field7", 0).And(). + EqualTo("$.field2.field3", true).Limit(3, 1).OrderBy("$.field2.field4.field5", true); + + /** + * @tc.steps: step3. use GetEntries interface to to get the resultSet in the 4 query objects. + * @tc.expected: step3. all of them return INVALID_QUERY_FORMAT. + */ + KvStoreResultSet *resultSet = nullptr; + EXPECT_EQ(delegate->GetEntries(query1, resultSet), DBStatus::INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query2, resultSet), DBStatus::INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query3, resultSet), DBStatus::INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query4, resultSet), DBStatus::INVALID_QUERY_FORMAT); + + /** + * @tc.steps: step4. use GetEntries interface get the records in the query objects. + * @tc.expected: step4. all of them return INVALID_QUERY_FORMAT. + */ + vector entriesGot; + EXPECT_EQ(delegate->GetEntries(query1, entriesGot), INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query2, entriesGot), INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query3, entriesGot), INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetEntries(query4, entriesGot), INVALID_QUERY_FORMAT); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +/** + * @tc.name: Query 019 + * @tc.desc: verify that GetCount interface of KvStoreNbDelegate class can't support OrderBy and + * Limit interface in query + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query019, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db of schema mode and put some schema data the db; + * @tc.expected: step1. create and put successfully. + */ + vector entries; + vector schemasValue = GenerateCombinationSchemaValue(); + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_COMBINATION_DEFINE); + + /** + * @tc.steps: step2. construct valid query object such as: + * query1: EqualTo on field3 = true and Order field5 by asec. + * query2: EqualTo on field3 = true and Limit(5, 1). + * @tc.expected: step2. construct successfully. + */ + Query query1 = Query::Select().EqualTo("$.field2.field3", true).OrderBy("$.field2.field4.field5", true); + Query query2 = Query::Select().EqualTo("$.field2.field3", true).Limit(5, 1); // get 5 records begin from 1 + + /** + * @tc.steps: step3. use GetCount interface of KvStoreNbDelegate class to check the count of query objects. + * @tc.expected: step3. all of them return INVALID_QUERY_FORMAT. + */ + int count = 0; + EXPECT_EQ(delegate->GetCount(query1, count), DBStatus::INVALID_QUERY_FORMAT); + EXPECT_EQ(delegate->GetCount(query2, count), DBStatus::INVALID_QUERY_FORMAT); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +void MakeSchemaValues(vector &schemasValue) +{ + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\":\"fxy\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"abc\",\"field2\":{\"field3\":\"fxz\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\": null ,\"field2\":{\"field3\":\"fxw\",\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"bxz\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); + schemasValue.push_back("{\"field1\":\"TRUE\",\"field2\":{\"field3\": null ,\"field4\":[]}}"); +} +/** + * @tc.name: Query 020 + * @tc.desc: veriry query executed normal after delete records. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query020, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + MakeSchemaValues(schemasValue); + /** + * @tc.steps: step1. create schema db and put 5 entries which has valid schema constructor + * and has the value given to db. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_STRING); + + /** + * @tc.steps: step2. call GetEntries with query=Select().NotIn(field1,{}) or query=Select().In(field1,{}) or + * query=Select().Like(field1,{}), and check the result with GetEntries. + * @tc.expected: step2. return 4 entries if use NotIn, return NOT_FOUND if use In and Like. + */ + vector scope; + vector entriesResult; + Query query1 = Query::Select().NotIn("$.field1", scope); + Query query2 = Query::Select().In("$.field1", scope); + Query query3 = Query::Select().Like("$.field2.field3", std::string()); + Query query4 = Query::Select().NotLike("$.field2.field3", std::string()); + EXPECT_EQ(delegate->GetEntries(query1, entriesResult), OK); + EXPECT_EQ(delegate->GetEntries(query2, entriesResult), NOT_FOUND); + EXPECT_EQ(delegate->GetEntries(query3, entriesResult), NOT_FOUND); + EXPECT_EQ(delegate->GetEntries(query4, entriesResult), OK); + + /** + * @tc.steps: step3. call GetEntries with query=Select().Like(field1,scope) that scope.size()=50000B, 50001B, + * 4M and check the result with GetEntries. + * @tc.expected: step3. return error code correspondingly. + */ + string queryValidStr(LIKE_AND_NOTLIKE_MAX_LENGTH, 'a'); + Query queryLike = Query::Select().Like("$.field2.field3", queryValidStr); + EXPECT_EQ(delegate->GetEntries(queryLike, entriesResult), NOT_FOUND); + Query queryNotLike = Query::Select().NotLike("$.field2.field3", queryValidStr); + EXPECT_EQ(delegate->GetEntries(queryNotLike, entriesResult), OK); + vector queryOvermaxStr; + string queryInvalidStr1(LIKE_AND_NOTLIKE_MAX_LENGTH + 1, 'b'); + queryOvermaxStr.push_back(queryInvalidStr1); + string queryInvalidStr2(FOUR_M_LONG_STRING, 'c'); + queryOvermaxStr.push_back(queryInvalidStr2); + for (auto const &it : queryOvermaxStr) { + Query queryLike = Query::Select().Like("$.field2.field3", it); + EXPECT_EQ(delegate->GetEntries(queryLike, entriesResult), OVER_MAX_LIMITS); + Query queryNotLike = Query::Select().NotLike("$.field2.field3", it); + EXPECT_EQ(delegate->GetEntries(queryNotLike, entriesResult), OVER_MAX_LIMITS); + } + + /** + * @tc.steps: step4. call GetEntries with query=Select().NotIn(field1,scope) or query=Select().In(field1,scope) + * that scope.size()= 128B, 129B and check the result with GetEntries. + * @tc.expected: step4. return error code correspondingly. + */ + vector scopeValid; + scopeValid.assign(IN_AND_NOTIN_MAX_LENGTH, "d"); + Query query5 = Query::Select().NotIn("$.field1", scopeValid); + Query query6 = Query::Select().In("$.field1", scopeValid); + EXPECT_EQ(delegate->GetEntries(query5, entriesResult), OK); + EXPECT_EQ(delegate->GetEntries(query6, entriesResult), NOT_FOUND); + + vector scopeInValid; + scopeInValid.assign(IN_AND_NOTIN_MAX_LENGTH + 1, "e"); + Query query7 = Query::Select().NotIn("$.field1", scopeInValid); + Query query8 = Query::Select().In("$.field1", scopeInValid); + EXPECT_EQ(delegate->GetEntries(query7, entriesResult), OVER_MAX_LIMITS); + EXPECT_EQ(delegate->GetEntries(query8, entriesResult), OVER_MAX_LIMITS); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} + +void MakeTenSchemaValues(vector &schemasValue) +{ + for (int recordIndex = 0; recordIndex < TEN_RECORDS; ++recordIndex) { + string val = "{\"field1\":" + string("\"") + string(1, 'a' + recordIndex) + "\"" + + ",\"field2\":{\"field3\":\"fxy\",\"field4\":[]}}"; + schemasValue.push_back(val); + } +} +/** + * @tc.name: Query 021 + * @tc.desc: verify the record num of In query after delete several records. + * @tc.type: FUNC + * @tc.require: SR000DR9JP + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbPredicateQueryTest, Query021, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + vector entries; + vector schemasValue; + MakeTenSchemaValues(schemasValue); + /** + * @tc.steps: step1. create schema db and put 10 records with valid schema. + * @tc.expected: step1. create and put successfully. + */ + PrepareSchemaDBAndData(delegate, manager, schemasValue, entries, VALID_DEFINE_STRING); + + /** + * @tc.steps: step2. call GetEntries with query=Select().GreaterThanOrEqualTo("$.field1",{}) or + * query=Select().EqualTo("$.field3", "fxy"). + * @tc.expected: step2. return 5 records. + */ + for (int delRecordNum = ONE_RECORD; delRecordNum <= FIVE_RECORDS; delRecordNum++) { + Entry entry = entries.back(); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, entry.key), OK); + entries.pop_back(); + } + vector entriesResult; + KvStoreResultSet *resultSet = nullptr; + Query query = Query::Select().GreaterThanOrEqualTo("$.field1", "f").Or().EqualTo("$.field2.field3", "fxy"); + EXPECT_EQ(delegate->GetEntries(query, entriesResult), OK); + EXPECT_EQ(delegate->GetEntries(query, resultSet), OK); + EXPECT_TRUE(entriesResult.size() == FIVE_RECORDS); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, g_predicateOption.isMemoryDb)); +} +} +#endif \ No newline at end of file diff --git a/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_schema_test.cpp b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_schema_test.cpp new file mode 100755 index 000000000..ab482f83b --- /dev/null +++ b/services/distributeddataservice/test/moduletest/common/distributeddb/src/distributeddb_nb_schema_test.cpp @@ -0,0 +1,2417 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 +#include +#include +#include +#include +#include + +#include "types.h" +#include "kv_store_delegate.h" +#include "kv_store_nb_delegate.h" +#include "kv_store_delegate_manager.h" +#include "distributed_test_tools.h" +#include "distributeddb_nb_test_tools.h" +#include "distributeddb_data_generator.h" +#include "distributeddb_schema_test_tools.h" + +using namespace std; +using namespace chrono; +using namespace testing; +#if defined TESTCASES_USING_GTEST_EXT +using namespace testing::ext; +#endif +using namespace std::placeholders; +using namespace DistributedDB; +using namespace DistributedDBDataGenerator; + +namespace DistributeddbNbSchemaDb { +KvStoreNbDelegate *g_nbSchemaDelegate = nullptr; +KvStoreDelegateManager *g_manager = nullptr; + +// invalid filed number: 0 or 257 +void GetInvalidFieldNumber(vector &defineResult) +{ + defineResult.push_back(INVALID_DEFINE_1); + LongDefine param; + param.recordNum = RECORDNUM; + param.recordSize = RECORDSIZE; + param.prefix = 'f'; + string invalidField; + GetLongSchemaDefine(param, invalidField); + defineResult.push_back(invalidField); +} + +void GetInvalidDefault(vector &defineResult) +{ + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = OVER_MAXSIZE; + param.prefix = 'k'; + string invalidField; + GetLongSchemaDefine(param, invalidField); + defineResult.push_back(invalidField); + // the level of field is over 4 + defineResult.push_back(INVALID_DEFINE_2); +} + +vector GetInvalidDefine(SchemaDefine &validDefine, SchemaDefine &invalidDefine) +{ + string define; + string invalidField(KEY_SIXTYFOUR_BYTE + 1, 'a'); + define = define + "\"" + invalidField + "\""; + invalidDefine.field.push_back(define); + vector defineResult; + // invalid field name + for (const auto &iter : invalidDefine.field) { + define.clear(); + define = define + "{" + iter + ":" + "\"" + validDefine.type.at(0) + "," + validDefine.notnull.at(0) + + "," + validDefine.defaultValue.at(0) + "\"" + "}"; + defineResult.push_back(define); + } + // invalid filed number: 0 or 257 + GetInvalidFieldNumber(defineResult); + // invalid field attributes: type + for (const auto &iter : invalidDefine.type) { + define.clear(); + define = define + "{" + validDefine.field.at(0) + ":" + "\"" + iter + "," + validDefine.notnull.at(0) + + "\"" + "}"; + defineResult.push_back(define); + } + // invalid field attributes: not_null + for (const auto &iter : invalidDefine.notnull) { + define.clear(); + define = define + "{" + validDefine.field.at(0) + ":" + "\"" + validDefine.type.at(0) + "," + iter + + "," + validDefine.defaultValue.at(0) + "\"" + "}"; + defineResult.push_back(define); + } + // invalid field attributes: DEFAULT x + for (unsigned int index1 = 0; index1 < validDefine.type.size(); index1++) { + for (unsigned int index2 = 0; index2 < invalidDefine.defaultValue.size(); index2++) { + define.clear(); + if (index1 == 0 && ((index2 == INDEX_FIFY) || (index2 == INDEX_SIX) || (index2 == INDEX_NINE) || + (index2 == INDEX_TEN))) { + continue; + } else { + define = define + "{" + validDefine.field.at(0) + ":" + "\"" + validDefine.type[index1] + "," + + validDefine.notnull.at(0) + "," + invalidDefine.defaultValue[index2] + "\"" + "}"; + defineResult.push_back(define); + } + } + } + // invalid field attributes: mix of type,not_null,DEFAULT x and DEFAULT x over the range of INTEGER/LONG/DOUBLE + for (const auto &iter : INVALID_ATTRIBUTES) { + define.clear(); + define = define + "{" + validDefine.field.at(0) + ":" + iter + "}"; + defineResult.push_back(define); + } + // DEFAULT x over 4K is invalid + GetInvalidDefault(defineResult); + return defineResult; +} + +class DistributeddbNbSchemaDbTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +private: +}; + +void DistributeddbNbSchemaDbTest::SetUpTestCase(void) +{ +} + +void DistributeddbNbSchemaDbTest::TearDownTestCase(void) +{ +} + +void DistributeddbNbSchemaDbTest::SetUp(void) +{ + RemoveDir(NB_DIRECTOR); + + UnitTest *test = UnitTest::GetInstance(); + ASSERT_NE(test, nullptr); + const TestInfo *testinfo = test->current_test_info(); + ASSERT_NE(testinfo, nullptr); + string testCaseName = string(testinfo->name()); + MST_LOG("[SetUp] test case %s is start to run", testCaseName.c_str()); +} + +void DistributeddbNbSchemaDbTest::TearDown(void) +{ + MST_LOG("TearDownTestCase after case."); + RemoveDir(NB_DIRECTOR); +} + +/** + * @tc.name: BuildSchemaDb 001 + * @tc.desc: Verify that single-ver db can support create schema db if the schema is valid. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, BuildSchemaDb001, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create db with valid schema. + * @tc.expected: step1. create successfully. + */ + Schema validSchema; + validSchema.version = VALID_VERSION; + validSchema.mode = VALID_MODE; + validSchema.define = VALID_DEFINE; + validSchema.index = VALID_INDEX; + vector validSchemaStr = GetValidSchema(validSchema, true); + Option option = g_option; + option.isMemoryDb = false; + int count = 0; + for (const auto &iter : validSchemaStr) { + MST_LOG("######## ######## ######## BuildSchemaDb001 : SubCase[%d] BEGIN ######## ######## ########", count); + option.schema = iter; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); + MST_LOG("######## ######## ######## BuildSchemaDb001 : SubCase[%d] END ######## ######## ########", count++); + } +} + +/** + * @tc.name: BuildSchemaDb 002 + * @tc.desc: Verify that single-ver db can't create schema db if the schema is invalid. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, BuildSchemaDb002, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Schema validSchema, invalidSchema; + validSchema.version = VALID_VERSION; + validSchema.mode = VALID_MODE; + validSchema.define = VALID_DEFINE; + validSchema.index = VALID_INDEX; + + SchemaDefine invalidDefine, validDefine; + invalidDefine.field = INVALID_DEFINE_FIELD; + invalidDefine.type = INVALID_TYPE; + invalidDefine.notnull = INVALID_NOTNULL; + invalidDefine.defaultValue = INVALID_DEFAULT; + validDefine.field = VALID_DEFINE_FIELD; + validDefine.type = VALID_TYPE; + validDefine.notnull = VALID_NOTNULL; + validDefine.defaultValue = VALID_DEFAULT; + vector defineRes = GetInvalidDefine(validDefine, invalidDefine); + invalidSchema.version = INVALID_VERSION; + invalidSchema.mode = INVALID_MODE; + invalidSchema.define = defineRes; + invalidSchema.index = INALID_INDEX; + map> schemaRes1 = GetInvalidSchema(invalidSchema, validSchema, false); + map> schemaRes2 = GetInvalidSchema(invalidSchema, validSchema, true); + + /** + * @tc.steps: step1. create db with invalid schema without SCHEMA_INDEX. + * @tc.expected: step1. create failed and return INVALID_SCHEMA. + */ + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + int count = 0; + for (int index = 0; index < SCHEMA_INDEX; index++) { + for (const auto &iter : schemaRes1[index]) { + option.schema = iter; + MST_LOG("######## ######## BuildSchemaDb002 : SubCase[%d] BEGIN ######## ######## ########", count); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter3, option); + MST_LOG("The invalid field is %d", index); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, INVALID_SCHEMA); + MST_LOG("######## ######## BuildSchemaDb002 : SubCase[%d] END ######## ######## ########", count++); + } + } + /** + * @tc.steps: step2. create db with invalid schema with invalid SCHEMA_INDEX. + * @tc.expected: step2. create failed and return INVALID_SCHEMA. + */ + for (int index = 0; index < SCHEMA_INDEX; index++) { + for (const auto &iter : schemaRes2[index]) { + option.schema = iter; + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, INVALID_SCHEMA); + } + } +} + +/** + * @tc.name: BuildSchemaDb 003 + * @tc.desc: Verify that single-ver memory db can't support create schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, BuildSchemaDb003, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + /** + * @tc.steps: step1. create memore db with valid schema. + * @tc.expected: step1. create failed and return NOT_SUPPORT. + */ + Schema validSchema; + validSchema.version = VALID_VERSION; + validSchema.mode = VALID_MODE; + validSchema.define = VALID_DEFINE; + validSchema.index = VALID_INDEX; + vector validSchemaStr = GetValidSchema(validSchema, true); + Option option = g_option; + option.isMemoryDb = true; + DBStatus status; + for (const auto &iter : validSchemaStr) { + option.schema = iter; + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, NOT_SUPPORT); + } +} + +/** + * @tc.name: OpenSchemaDb 001 + * @tc.desc: Verify that can open with schema when the kvstore is not schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb001, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + + /** + * @tc.steps: step1. reopen db with schema of STRICT mode and put(k1,value). + * @tc.expected: step1. reopen and put successfully. + */ + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = RECORDSIZE; + param.prefix = 'k'; + string invalidField; + GetLongSchemaDefine(param, invalidField); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, invalidField, VALID_INDEX_1); + option.createIfNecessary = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + Value valueSchema(invalidField.begin(), invalidField.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), OK); + + /** + * @tc.steps: step2. Get(k1) and delete(k1). + * @tc.expected: step2. Get(k1)=value and delete successfully. + */ + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, KEY_1), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + + /** + * @tc.steps: step3. reopen db with no schema and put(k1,v1),delete(k1),Get(k1). + * @tc.expected: step3. reopen successfully and put,delete return READ_ONLY,Get return NOT_FOUND. + */ + option.schema.clear(); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), READ_ONLY); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, KEY_1), READ_ONLY); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), NOT_FOUND); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 002 + * @tc.desc: Verify that single-ver db can't reopen schema db if the schema is invalid when the kvstore is + * not a schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb002, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + + Schema validSchema, invalidSchema; + validSchema.version = VALID_VERSION; + validSchema.mode = VALID_MODE; + validSchema.define = VALID_DEFINE; + validSchema.index = VALID_INDEX; + + SchemaDefine invalidDefine, validDefine; + invalidDefine.field = INVALID_DEFINE_FIELD; + invalidDefine.type = INVALID_TYPE; + invalidDefine.notnull = INVALID_NOTNULL; + invalidDefine.defaultValue = INVALID_DEFAULT; + validDefine.field = VALID_DEFINE_FIELD; + validDefine.type = VALID_TYPE; + validDefine.notnull = VALID_NOTNULL; + validDefine.defaultValue = VALID_DEFAULT; + vector defineRes = GetInvalidDefine(validDefine, invalidDefine); + invalidSchema.version = INVALID_VERSION; + invalidSchema.mode = INVALID_MODE; + invalidSchema.define = defineRes; + invalidSchema.index = INALID_INDEX; + map> schemaRes1 = GetInvalidSchema(invalidSchema, validSchema, false); + map> schemaRes2 = GetInvalidSchema(invalidSchema, validSchema, true); + + /** + * @tc.steps: step1. reopen db with invalid schema without SCHEMA_INDEX. + * @tc.expected: step1. reopen failed and return INVALID_SCHEMA. + */ + option.createIfNecessary = false; + DBStatus status; + for (int index = 0; index < SCHEMA_INDEX; index++) { + for (const auto &iter : schemaRes1[index]) { + option.schema = iter; + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + MST_LOG("The invalid field is %d", index); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, INVALID_SCHEMA); + } + } + /** + * @tc.steps: step2. reopen db with invalid schema with invalid SCHEMA_INDEX. + * @tc.expected: step2. reopen failed and return INVALID_SCHEMA. + */ + for (int index = 0; index < SCHEMA_INDEX; index++) { + for (const auto &iter : schemaRes2[index]) { + option.schema = iter; + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, INVALID_SCHEMA); + } + } +} + +/** + * @tc.name: OpenSchemaDb 003 + * @tc.desc: Verify that can open with no schema when the kvstore is schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb003, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = RECORDSIZE; + param.prefix = 'k'; + string validField; + GetLongSchemaDefine(param, validField); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + Value valueSchema(validField.begin(), validField.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + /** + * @tc.steps: step1. reopen db with no schema. + * @tc.expected: step1. reopen successfully. + */ + option.createIfNecessary = false; + option.schema.clear(); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + /** + * @tc.steps: step2. Get(k1) and put(k1,v1), delete(k1). + * @tc.expected: step2. Get(k1)=value, put and delete failed. + */ + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, VALUE_1), READ_ONLY); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, KEY_1), READ_ONLY); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 004 + * @tc.desc: Verify that can open with schema when the kvstore is schema db even if the order of same + * level is different. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb004, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = RECORDSIZE; + param.prefix = 'k'; + string validField; + GetLongSchemaDefine(param, validField); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + Value valueSchema(validField.begin(), validField.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + /** + * @tc.steps: step1. reopen db with schema that the order of schema is different with kvstore. + * @tc.expected: step1. reopen successfully. + */ + option.createIfNecessary = false; + option.schema.clear(); + option.schema = option.schema + "{" + "\"SCHEMA_DEFINE\"" + ":" + validField + "," + + "\"SCHEMA_VERSION\"" + ":" + "\"" + VALID_VERSION_1 + "\"" + "," + + "\"SCHEMA_INDEXES\"" + ":" + VALID_INDEX_1 + "," + + "\"SCHEMA_MODE\"" + ":" + "\"" + VALID_MODE_1 + "\"" + "}"; + MST_LOG("schema is %s", option.schema.c_str()); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + /** + * @tc.steps: step2. Get(k1) and put(k1,v1), delete(k1). + * @tc.expected: step2. Get(k1)=value1, put and delete successfully. + */ + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema); + string value2 = "{\"field0\":\"fxy\"}"; + Value valueSchema2(value2.begin(), value2.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema2), OK); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate, KEY_1), OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 005 + * @tc.desc: Verify that can't open with schema when the schema is mismatch with the original kvstore schema. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb005, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + LongDefine param; + param.recordNum = ONE_RECORD; + param.recordSize = RECORDSIZE; + param.prefix = 'k'; + string validField; + GetLongSchemaDefine(param, validField); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, validField, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + /** + * @tc.steps: step1. reopen db with schema which mode is different from the original kvstore schema. + * @tc.expected: step1. reopen failed and return SCHEMA_MISMATCH. + */ + option.createIfNecessary = false; + option.schema.clear(); + DBStatus status; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + /** + * @tc.steps: step2. reopen db with schema which define fields is more than the original kvstore schema. + * @tc.expected: step2. reopen failed and return SCHEMA_MISMATCH.(extra field has not null but no default) + */ + string validField2; + param.recordNum = TWO_RECORD; + GetLongSchemaDefine(param, validField2); + std::string toBeErased = ",DEFAULT 'kkk2'"; + auto iter = std::search(validField2.begin(), validField2.end(), toBeErased.begin(), toBeErased.end()); + validField2.erase(iter, iter + toBeErased.size()); + option.schema.clear(); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, validField2, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. reopen db with schema which SCHEMA_INDEX is not null. + * @tc.expected: step3. reopen failed and return SCHEMA_MISMATCH.(extra field has not null but no default) + */ + option.schema.erase(option.schema.size() - 3, 3); // erase 3 chars that starting at 3 st last. + option.schema.append("[\"$.field0\"]}"); + MST_LOG("option.schema is %s", option.schema.c_str()); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. reopen db with schema which schema_index is absent. + * @tc.expected: step4. reopen successfully. + */ + option.schema.clear(); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, validField, VALID_INDEX_1); + option.schema.erase(option.schema.size() - 21, 20); // erase 20 char that starting at 21 st last. + MST_LOG("schema is %s", option.schema.c_str()); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +void ReopenDBWithDiffSchema(string &schemaDefine, Option &option, + KvStoreDelegateManager *&manager, KvStoreNbDelegate *&delegate) +{ + option.schema.clear(); + DBStatus status; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_TRUE(manager == nullptr && delegate == nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); +} + +/** + * @tc.name: OpenSchemaDb 006 + * @tc.desc: Verify that can't open with schema when the schema_define attributes are mismatch with the + * original kvstore schema. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb006, TestSize.Level1) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + string schemaDefine1 = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine1, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate) == OK); + delegate = nullptr; + /** + * @tc.steps: step1. reopen db with schema which schema_define's type is different from the original kvstore schema. + * @tc.expected: step1. reopen failed and return SCHEMA_MISMATCH. + */ + option.createIfNecessary = false; + string schemaDefine2 = "{\"field0\":\"LONG,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + ReopenDBWithDiffSchema(schemaDefine2, option, manager, delegate); + /** + * @tc.steps: step2. reopen db with schema which schema_define's not_null is different from the + * original kvstore schema. + * @tc.expected: step2. reopen failed and return SCHEMA_MISMATCH. + */ + string schemaDefine3 = "{\"field0\":\"INTEGER,DEFAULT 10\",\"field1\":[]}"; + ReopenDBWithDiffSchema(schemaDefine3, option, manager, delegate); + /** + * @tc.steps: step3. reopen db with schema which schema_define's default x is different from the + * original kvstore schema. + * @tc.expected: step3. reopen failed and return SCHEMA_MISMATCH. + */ + string schemaDefine4 = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 11\",\"field1\":[]}"; + ReopenDBWithDiffSchema(schemaDefine4, option, manager, delegate); + string schemaDefine5 = "{\"field0\":\"INTEGER,NOT NULL\",\"field1\":[]}"; + ReopenDBWithDiffSchema(schemaDefine5, option, manager, delegate); + /** + * @tc.steps: step4. reopen db with schema which schema_define's Json Array is different from the + * original kvstore schema. + * @tc.expected: step4. reopen failed and return SCHEMA_MISMATCH. + */ + string schemaDefine6 = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":{}}"; + ReopenDBWithDiffSchema(schemaDefine6, option, manager, delegate); + KvStoreDelegateManager *manager1 = new (std::nothrow) KvStoreDelegateManager(APP_ID_2, USER_ID_2); + ASSERT_NE(manager1, nullptr); + EXPECT_EQ(manager1->SetKvStoreConfig({ .dataDir = NB_DIRECTOR }), OK); + EXPECT_EQ(manager1->DeleteKvStore(STORE_ID_2), OK); + delete manager1; + manager1 = nullptr; +} + +/** + * @tc.name: OpenSchemaDb 007 + * @tc.desc: Verify that open new db conn must be the same as first time(common) when the original db is common db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb007, TestSize.Level1) +{ + map delegate; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + delegate["OriNormalDB"] = nullptr; + delegate["OriNormalDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OriNormalDB"] != nullptr); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate["OriNormalDB"], KEY_1, VALUE_1), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate["OriNormalDB"]) == OK); + + /** + * @tc.steps: step1. get delegate1 without schema. + * @tc.expected: step1. get successfully. + */ + option.createIfNecessary = false; + delegate["OpenNormalDB"] = nullptr; + delegate["OpenNormalDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OpenNormalDB"] != nullptr); + + /** + * @tc.steps: step2. get delegate2 with schema. + * @tc.expected: step2. get failed and return SCHEMA_MISMATCH. + */ + string validField = "{\"field0\":\"INTEGER,DEFAULT 10\",\"field1\":[]}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + DBStatus status; + delegate["OpenSchemaDB"] = nullptr; + delegate["OpenSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate["OpenSchemaDB"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. get delegate3 without schema. + * @tc.expected: step3. get successfully. + */ + option.schema.clear(); + delegate["OpenNormalDB2"] = nullptr; + delegate["OpenNormalDB2"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OpenNormalDB2"] != nullptr); + + /** + * @tc.steps: step4. put(k1,v2),Get(k1),delete(k1) with delegate3. + * @tc.expected: step4. put and delete successfully, Get(k1)=v2,. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate["OpenNormalDB2"], KEY_1, VALUE_2), OK); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate["OpenNormalDB2"], KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, VALUE_2); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate["OpenNormalDB2"], KEY_1), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate["OpenNormalDB"]) == OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate["OpenNormalDB2"], STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 008 + * @tc.desc: Verify that open new db conn must be the same as first time(common) when the original db is schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb008, TestSize.Level1) +{ + map delegate; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + string validField = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate["OriSchemaDB"] = nullptr; + delegate["OriSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OriSchemaDB"] != nullptr); + string valueStr = "{\"field0\":15,\"field1\":[20]}"; + Value valueSchema(valueStr.begin(), valueStr.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate["OriSchemaDB"], KEY_1, valueSchema), OK); + EXPECT_TRUE(manager->CloseKvStore(delegate["OriSchemaDB"]) == OK); + + /** + * @tc.steps: step1. get delegate1 without schema. + * @tc.expected: step1. get successfully. + */ + option.createIfNecessary = false; + option.schema.clear(); + delegate["OpenNormalDB"] = nullptr; + delegate["OpenNormalDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OpenNormalDB"] != nullptr); + + /** + * @tc.steps: step2. get delegate2 with schema. + * @tc.expected: step2. get failed and return SCHEMA_MISMATCH. + */ + DBStatus status; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate["OpenSchemaDB"] = nullptr; + delegate["OpenSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate["OpenSchemaDB"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. get delegate3 without schema. + * @tc.expected: step3. get successfully. + */ + option.schema.clear(); + delegate["OpenNormalDB2"] = nullptr; + delegate["OpenNormalDB2"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OpenNormalDB2"] != nullptr); + + /** + * @tc.steps: step4. put(k1,v2),Get(k1),delete(k1) with delegate3. + * @tc.expected: step4. put and delete successfully, Get(k1)=v2,. + */ + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate["OpenNormalDB2"], KEY_1, VALUE_2), READ_ONLY); + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate["OpenNormalDB2"], KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema); + EXPECT_EQ(DistributedDBNbTestTools::Delete(*delegate["OpenNormalDB2"], KEY_1), READ_ONLY); + EXPECT_TRUE(manager->CloseKvStore(delegate["OpenNormalDB"]) == OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate["OpenNormalDB2"], STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 009 + * @tc.desc: Verify that open new db conn must be the same as first time(schema) when the original db is common db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb009, TestSize.Level1) +{ + map delegate; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + delegate["OriNormalDB"] = nullptr; + delegate["OriNormalDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OriNormalDB"] != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegate["OriNormalDB"]) == OK); + + /** + * @tc.steps: step1. get delegate1 with schema. + * @tc.expected: step1. get successfully. + */ + option.createIfNecessary = false; + string validField = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate["FirstOpenSchema"] = nullptr; + delegate["FirstOpenSchema"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["FirstOpenSchema"] != nullptr); + + /** + * @tc.steps: step2. get delegate2 without schema. + * @tc.expected: step2. get failed and return SCHEMA_MISMATCH. + */ + option.schema.clear(); + DBStatus status; + delegate["OpenNormal"] = nullptr; + delegate["OpenNormal"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate["OpenNormal"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. get delegate3 with schema2. + * @tc.expected: step3. get failed and return SCHEMA_MISMATCH. + */ + option.schema.clear(); + string validField1 = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField1, VALID_INDEX_1); + delegate["OpenDiffSchema"] = nullptr; + delegate["OpenDiffSchema"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate["OpenDiffSchema"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. get delegate4 with schema. + * @tc.expected: step4.get successfully. + */ + option.schema.clear(); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegate["OpenSameSchema"] = nullptr; + delegate["OpenSameSchema"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["OpenSameSchema"] != nullptr); + // close all opened delegates. + EXPECT_TRUE(manager->CloseKvStore(delegate["OpenSameSchema"]) == OK); + EXPECT_TRUE(manager->CloseKvStore(delegate["FirstOpenSchema"]) == OK); + delegate.clear(); + + /** + * @tc.steps: step5. get delegate5 without schema after close all delegates that were opened. + * @tc.expected: step4.get successfully. + */ + option.schema.clear(); + delegate["ReopenNormal"] = nullptr; + delegate["ReopenNormal"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate["ReopenNormal"] != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate["ReopenNormal"], STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: OpenSchemaDb 010 + * @tc.desc: Verify that open new db conn must be the same as first time(schema) when the original db is schema db. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, OpenSchemaDb010, TestSize.Level1) +{ + std::map delegateGroup; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + string validField = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\",\"field1\":[]}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegateGroup["OriSchemaDB"] = nullptr; + delegateGroup["OriSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegateGroup["OriSchemaDB"] != nullptr); + EXPECT_TRUE(manager->CloseKvStore(delegateGroup["OriSchemaDB"]) == OK); + delegateGroup["OriSchemaDB"] = nullptr; + delete manager; + manager = nullptr; + + /** + * @tc.steps: step1. get delegate1 with schema. + * @tc.expected: step1. get successfully. + */ + option.createIfNecessary = false; + delegateGroup["OriSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegateGroup["OriSchemaDB"] != nullptr); + + /** + * @tc.steps: step2. get delegate2 without schema. + * @tc.expected: step2. get failed and return SCHEMA_MISMATCH. + */ + delegateGroup["NormalDB"] = nullptr; + option.schema.clear(); + DBStatus status; + delegateGroup["NormalDB"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, + g_dbParameter2, option); + EXPECT_EQ(delegateGroup["NormalDB"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. get delegate3 with schema2. + * @tc.expected: step3. get failed and return SCHEMA_MISMATCH. + */ + delegateGroup["DiffSchemaDB"] = nullptr; + option.schema.clear(); + string validField1 = "{\"field0\":\"INTEGER,NOT NULL,DEFAULT 10\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField1, VALID_INDEX_1); + delegateGroup["DiffSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, + g_dbParameter2, option); + EXPECT_EQ(delegateGroup["DiffSchemaDB"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. get delegate4 with schema. + * @tc.expected: step4.get successfully. + */ + delegateGroup["SameSchemaDB"] = nullptr; + option.schema.clear(); + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, validField, VALID_INDEX_1); + delegateGroup["SameSchemaDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegateGroup["SameSchemaDB"] != nullptr); + // Close all opened delegate + EXPECT_TRUE(manager->CloseKvStore(delegateGroup["OriSchemaDB"]) == OK); + EXPECT_TRUE(manager->CloseKvStore(delegateGroup["SameSchemaDB"]) == OK); + delegateGroup.clear(); + + /** + * @tc.steps: step5. get delegate5 without schema after close all delegates that were opened. + * @tc.expected: step4.get successfully. + */ + delegateGroup["NormalDB"] = nullptr; + option.schema.clear(); + delegateGroup["NormalDB"] = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegateGroup["NormalDB"] != nullptr); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegateGroup["NormalDB"], STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SchemaPut 001 + * @tc.desc: Verify that PUT value will return OK if the format is the same as define of schema. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut001, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_2); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1. put(k1,value1) that value1's format is the same as define of schema. + * @tc.expected: step1. put successfully. + */ + string valueRes1; + valueRes1 = valueRes1 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "}"; + Value valueSchema1(valueRes1.begin(), valueRes1.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema1), OK); + + /** + * @tc.steps: step2. put(k2,value2) that value2's format is the same as define of schema except there exist an + * repeat field in the same level. + * @tc.expected: step2. put successfully. + */ + string valueRes2; + valueRes2 = valueRes2 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "\"field17\": 20000000" + "}"; + Value valueSchema2(valueRes2.begin(), valueRes2.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema2), OK); + + /** + * @tc.steps: step3. put(k3,value3) that value3's format is the same as define of schema except the order of + * fields in the same level is being upset. + * @tc.expected: step3. put successfully. + */ + string valueRes3; + valueRes3 = valueRes3 + "{" + VALUE_MATCH_2 + "," + VALUE_MATCH_1 + "}"; + Value valueSchema3(valueRes3.begin(), valueRes3.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_3, valueSchema3), OK); + + /** + * @tc.steps: step4. Get(k1,k2,k3). + * @tc.expected: step4. Get(k1)=value1, Get(k2)=value2, Get(k3)=value3. + */ + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema2); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_3, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema3); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SchemaPut 002 + * @tc.desc: Verify that PUT value will return INVALID_VALUE_FIELDS if the format is different from define of schema + * when SCHEMA_MODE = STRICT. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut002, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1. put(k1,value1) that value1's field is less than define of schema. + * @tc.expected: step1. put ok(less field no notnull). + */ + string valueMatch = VALUE_MATCH_2; + valueMatch.erase(valueMatch.size() - 9, 9); // erase 9 chars starting at 9 st last. + string valueRes1; + valueRes1 = valueRes1 + "{" + VALUE_MATCH_1 + "," + valueMatch + "}"; + Value valueSchema1(valueRes1.begin(), valueRes1.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema1), OK); + + /** + * @tc.steps: step2. put(k2,value2) that value2's field is more than define of schema. + * @tc.expected: step2. put failed. + */ + string valueRes2; + valueRes2 = valueRes2 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "\"field19\": 20" + "}"; + Value valueSchema2(valueRes2.begin(), valueRes2.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema2), INVALID_VALUE_FIELDS); + + /** + * @tc.steps: step3. put(k3,value3) that there exist a field is not start with char or _ in value3. + * @tc.expected: step3. put failed. + */ + string valueRes3; + valueRes3 = valueRes3 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "\"19field\": 20" + "}"; + Value valueSchema3(valueRes3.begin(), valueRes3.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_3, valueSchema3), INVALID_VALUE_FIELDS); + + /** + * @tc.steps: step4. put(k4,value4) that there exist a field'level is upseted in value4. + * @tc.expected: step4. put failed(less field notnull). + */ + string valueRes = "\"_field1\":{\"field1\":\"abc\",\"field2\":null,\"field3\":{\"field6\":\"def\"," + "\"field5\":{\"field4\":\"fxy\",\"field7\":[]}}}"; + string valueRes4; + valueRes4 = valueRes4 + "{" + valueRes + "," + VALUE_MATCH_2 + "}"; + Value valueSchema4(valueRes4.begin(), valueRes4.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_4, valueSchema4), CONSTRAIN_VIOLATION); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +bool PutAfterReplace(string &operateStr, const string findStr, const vector replaceStr, + KvStoreNbDelegate *&delegate) +{ + if (findStr == "[]" || findStr == "{}") { + string valueMatch = operateStr; + if (valueMatch.find(findStr) == string::npos) { + MST_LOG("Can't find findStr in operateStr!"); + return false; + } + valueMatch.insert(valueMatch.find(findStr) + 1, "\"field8\"\"error\""); + string valueRes; + if (operateStr == VALUE_MATCH_1) { + valueRes = valueRes + "{" + valueMatch + "," + VALUE_MATCH_2 + "}"; + } else { + valueRes = valueRes + "{" + VALUE_MATCH_1 + "," + valueMatch + "}"; + } + Value valueSchema(valueRes.begin(), valueRes.end()); + MST_LOG("valueSchema is %s", valueRes.c_str()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), INVALID_FORMAT); + return true; + } + for (const auto &iter : replaceStr) { + string valueMatch = operateStr; + if (valueMatch.find(findStr) == string::npos) { + MST_LOG("Can't find findStr in operateStr!"); + return false; + } + valueMatch.replace(valueMatch.find(findStr), findStr.size(), iter); + string valueRes; + if (operateStr == VALUE_MATCH_1) { + valueRes = valueRes + "{" + valueMatch + "," + VALUE_MATCH_2 + "}"; + } else { + valueRes = valueRes + "{" + VALUE_MATCH_1 + "," + valueMatch + "}"; + } + Value valueSchema(valueRes.begin(), valueRes.end()); + MST_LOG("valueSchema is %s", valueRes.c_str()); + if (iter == "null") { + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), CONSTRAIN_VIOLATION); + } else if (iter == "0X2B") { + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), INVALID_FORMAT); + } else { + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema), INVALID_FIELD_TYPE); + } + } + return true; +} +#ifdef USING_NEW_VER_JSONCPP +/** + * @tc.name: SchemaPut 003 + * @tc.desc: Verify that PUT value will return error if the field attributes are different with define of schema + * when SCHEMA_MODE = STRICT. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut003, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1-2. put(k1,value1) that value1's type or not_null is different with define of schema. + * @tc.expected: step1-2. put failed. + */ + // the define of schema's type is string + string findStr1 = "\"abc\""; + vector replaceStr1 = {"123", "true", "false", "null"}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_1, findStr1, replaceStr1, delegate)); + + // the define of schema's type is bool + string findStr2 = "false"; + vector replaceStr2 = {"\"false\"", "\"true\"", "null"}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_2, findStr2, replaceStr2, delegate)); + + // the define of schema's type is INTEGER + string findStr3 = "-1000000"; + vector replaceStr3 = {"\"-1000000\"", "10.5", "-10.00001", std::to_string(INT32_MAX) + "1", + std::to_string(INT32_MIN) + "1", "0X2B", "null"}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_2, findStr3, replaceStr3, delegate)); + + // the define of schema's type is LONG + string findStr4 = "666"; + vector replaceStr4 = {"\"666\"", "10.5", "10.10", std::to_string(INT64_MAX) + "1", + std::to_string(INT64_MIN) + "1", "0X2B", + "null"}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_2, findStr4, replaceStr4, delegate)); + + // the define of schema's type is DOUBLE + string findStr5 = "-1.05e-4"; + vector replaceStr5 = {"\"-1.05e4\"", "1" + std::to_string(DBL_MAX), + "-1" + std::to_string(DBL_MAX), "0X2B", "null"}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_2, findStr5, replaceStr5, delegate)); + + /** + * @tc.steps: step3. put(k2,value2) that value2 isn't Json format. + * @tc.expected: step3. put failed. + */ + string valueRes; + valueRes = valueRes + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "}"; + Value valueSchema(valueRes.begin(), valueRes.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema), INVALID_FORMAT); + + /** + * @tc.steps: step4. put(k3,value3) that value3's Json array or Json object isn't Json format. + * @tc.expected: step4. put failed. + */ + string findStr6 = "[]"; + vector replaceStr6 = {""}; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_1, findStr6, replaceStr6, delegate)); + string findStr7 = "{}"; + EXPECT_TRUE(PutAfterReplace(VALUE_MATCH_2, findStr7, replaceStr6, delegate)); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} +#endif +/** + * @tc.name: SchemaPut 004 + * @tc.desc: Verify that PUT value will return OK if the format is the same as define of schema with + * SCHEMA_MODE = COMPATIBLE. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut004, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_2); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1. put(k1,value1) that value1's format is the same as define of schema. + * @tc.expected: step1. put successfully. + */ + string valueRes1; + valueRes1 = valueRes1 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "}"; + Value valueSchema1(valueRes1.begin(), valueRes1.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema1), OK); + + /** + * @tc.steps: step2. put(k2,value2) that value2's format is the same as define of schema except there exist an + * repeat field in the same level. + * @tc.expected: step2. put successfully. + */ + string valueRes2; + valueRes2 = valueRes2 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "\"field17\": 20000000" + "}"; + Value valueSchema2(valueRes2.begin(), valueRes2.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema2), OK); + + /** + * @tc.steps: step3. put(k3,value3) that value3's format is the same as define of schema except the order of + * fields in the same level is being upset. + * @tc.expected: step3. put successfully. + */ + string valueRes3; + valueRes3 = valueRes3 + "{" + VALUE_MATCH_2 + "," + VALUE_MATCH_1 + "}"; + Value valueSchema3(valueRes3.begin(), valueRes3.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_3, valueSchema3), OK); + + /** + * @tc.steps: step3. put(k4,value4) that value4's field is more than define of schema. + * @tc.expected: step3. put successfully. + */ + string valueRes4; + valueRes4 = valueRes4 + "{" + VALUE_MATCH_2 + "," + VALUE_MATCH_1 + "," + "\"field20\": 2e7" + "}"; + Value valueSchema4(valueRes4.begin(), valueRes4.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_4, valueSchema4), OK); + + /** + * @tc.steps: step4. Get(k1,k2,k3,k4). + * @tc.expected: step4. Get(k1)=value1, Get(k2)=value2, Get(k3)=value3, Get(k4)=value4. + */ + Value valueResult; + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_1, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema1); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_2, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema2); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_3, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema3); + EXPECT_EQ(DistributedDBNbTestTools::Get(*delegate, KEY_4, valueResult), OK); + EXPECT_EQ(valueResult, valueSchema4); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SchemaPut 005 + * @tc.desc: Verify that PUT value will return error if the format is different from define of schema + * when SCHEMA_MODE = COMPATIBLE. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut005, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1. put(k1,value1) that value1's field is less than define of schema. + * @tc.expected: step1. put ok(lack field no notnull). + */ + string valueMatch = VALUE_MATCH_2; + valueMatch.erase(valueMatch.size() - 9, 9); // erase 9 chars starting at 9 st last. + string valueRes1; + valueRes1 = valueRes1 + "{" + VALUE_MATCH_1 + "," + valueMatch + "}"; + Value valueSchema1(valueRes1.begin(), valueRes1.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema1), OK); + + /** + * @tc.steps: step2. put(k2,value2) that value2's field is more than define of schema. + * @tc.expected: step2. put failed. + */ + string valueRes2; + valueRes2 = valueRes2 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "\"19field\": 20" + "}"; + Value valueSchema2(valueRes2.begin(), valueRes2.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema2), OK); + + /** + * @tc.steps: step3. put(k3,value3) that there exist a field'level is upseted in value4. + * @tc.expected: step3. put failed(lack field notnull). + */ + string valueRes = "\"_field1\":{\"field1\":\"abc\",\"field2\":null,\"field3\":{\"field6\":\"def\"," + "\"field5\":{\"field4\":\"fxy\",\"field7\":[]}}}"; + string valueRes3; + valueRes3 = valueRes3 + "{" + valueRes + "," + VALUE_MATCH_2 + "}"; + Value valueSchema3(valueRes3.begin(), valueRes3.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_3, valueSchema3), CONSTRAIN_VIOLATION); + + /** + * @tc.steps: step4. put(k4,value4) that value4 isn't Json format. + * @tc.expected: step4. put failed. + */ + string valueRes4; + valueRes4 = valueRes4 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "," + "}"; + Value valueSchema4(valueRes4.begin(), valueRes4.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_4, valueSchema4), INVALID_FORMAT); + + /** + * @tc.steps: step4. put(k5,value5) that value5's Json array or Json object isn't Json format. + * @tc.expected: step4. put failed. + */ + string findStr = "[]"; + vector replaceStr = {""}; + PutAfterReplace(VALUE_MATCH_1, findStr, replaceStr, delegate); + string findStr1 = "{}"; + PutAfterReplace(VALUE_MATCH_2, findStr1, replaceStr, delegate); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} +#ifdef USING_NEW_VER_JSONCPP +/** + * @tc.name: SchemaPut 006 + * @tc.desc: Verify that PUT value will return error if the field attributes are different with define of schema + * when SCHEMA_MODE = COMPATIBLE. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut006, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1-2. put(k1,value1) that value1's type or not_null is different with define of schema. + * @tc.expected: step1-2. put failed. + */ + // the define of schema's type is string + string findStr1 = "\"abc\""; + vector replaceStr1 = {"123", "true", "false", "null"}; + PutAfterReplace(VALUE_MATCH_1, findStr1, replaceStr1, delegate); + + // the define of schema's type is bool + string findStr2 = "false"; + vector replaceStr2 = {"\"false\"", "\"true\"", "null"}; + PutAfterReplace(VALUE_MATCH_2, findStr2, replaceStr2, delegate); + + // the define of schema's type is INTEGER + string findStr3 = "-1000000"; + vector replaceStr3 = {"\"-1000000\"", "10.5", "-10.00001", std::to_string(INT32_MAX) + "1", + std::to_string(INT32_MIN) + "1", "0X2B", "null"}; + PutAfterReplace(VALUE_MATCH_2, findStr3, replaceStr3, delegate); + + // the define of schema's type is LONG + string findStr4 = "666"; + vector replaceStr4 = {"\"666\"", "10.5", "-10.10", std::to_string(INT64_MAX) + "1", + std::to_string(INT64_MIN) + "1", "0X2B", "null"}; + PutAfterReplace(VALUE_MATCH_2, findStr4, replaceStr4, delegate); + + // the define of schema's type is DOUBLE + string findStr5 = "-1.05e-4"; + vector replaceStr5 = {"\"-1.05e4\"", "1" + std::to_string(DBL_MAX), + "-1" + std::to_string(DBL_MAX), "0X2B", "null"}; + PutAfterReplace(VALUE_MATCH_2, findStr5, replaceStr5, delegate); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} +#endif +/** + * @tc.name: SchemaPut 007 + * @tc.desc: Verify that if there are duplicate fields in schema, the latter will prevail when put. + * @tc.type: FUNC + * @tc.require: SR000DR9JO + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaPut007, TestSize.Level0) +{ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, VALID_DEFINE_1, VALID_INDEX_2); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step1. put(k1,value1) that value1 transfer all the value that repeat field in the same level. + * @tc.expected: step1. put failed. + */ + string valueRes1; + valueRes1 = valueRes1 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + ",\"field1\": \"false\"" + "}"; + Value valueSchema1(valueRes1.begin(), valueRes1.end()); + MST_LOG("valueRes1 is %s", valueRes1.c_str()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_1, valueSchema1), INVALID_FIELD_TYPE); + + /** + * @tc.steps: step2.4. put(k2,value2) that value2's format is the same as define of schema. + * @tc.expected: step2.4. put successfully. + */ + string valueRes2; + valueRes2 = valueRes2 + "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + "}"; + Value valueSchema2(valueRes2.begin(), valueRes2.end()); + MST_LOG("valueRes2 is %s", valueRes2.c_str()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_2, valueSchema2), OK); + + /** + * @tc.steps: step3. put(k3,value3) that value3 transfer only one that repeat field in the different level. + * @tc.expected: step3. put failed(field require not null but has default value). + */ + string deleteStr = VALUE_MATCH_1; + string findStr = "\"field1\":\"abc\","; + deleteStr.erase(deleteStr.find(findStr), findStr.size()); + string valueRes3; + valueRes3 = valueRes3 + "{" + deleteStr + "," + VALUE_MATCH_2 + "}"; + Value valueSchema3(valueRes3.begin(), valueRes3.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_3, valueSchema3), OK); + + /** + * @tc.steps: step5. put(k4,value4) that value4's Json Array has large fields so that the total fields are + * over 256. + * @tc.expected: step5. put successfully. + */ + LongDefine param; + param.recordNum = TWO_FIVE_SIX_RECORDS; + param.recordSize = RECORDSIZE; + param.prefix = 'k'; + string longField; + GetLongSchemaDefine(param, longField); + string valueMatch = VALUE_MATCH_1; + valueMatch.insert(valueMatch.find("[]") + 1, longField); + string valueRes4; + valueRes4 = valueRes4 + "{" + valueMatch + "," + VALUE_MATCH_2 + "}"; + Value valueSchema4(valueRes4.begin(), valueRes4.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_4, valueSchema4), OK); + + /** + * @tc.steps: step6. put(k5,value5) that value5's Json Object has large fields so that the total fields are + * over 256. + * @tc.expected: step6. put successfully. + */ + string valueMatch2 = VALUE_MATCH_2; + longField.erase(0, 1); + longField.erase(longField.size() - 1, 1); + valueMatch2.insert(valueMatch2.find("{}") + 1, longField); + string valueRes5; + valueRes5 = valueRes5 + "{" + VALUE_MATCH_1 + "," + valueMatch2 + "}"; + Value valueSchema5(valueRes5.begin(), valueRes5.end()); + EXPECT_EQ(DistributedDBNbTestTools::Put(*delegate, KEY_5, valueSchema5), OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +bool SchemaIndexQuery(const DBParameters parameters, const std::string dbPath, const std::string schemaIndex) +{ + int count = 0; + std::string identifier = parameters.userId + "-" + parameters.appId + "-" + parameters.storeId; + std::string hashIdentifierRes = TransferStringToHashHexString(identifier); + const std::string mainDbName = dbPath + hashIdentifierRes + DATABASE_INFOR_FILE; + EncrypteAttribute attribute = {g_option.isEncryptedDb, g_option.passwd}; + + const std::string SCHEMA_INDEX_QUERY_SQL = "select count(*) from sqlite_master where name= \'" + + schemaIndex + "\' and type = \'index\'"; + + DistributedTestTools::QuerySpecifiedData(mainDbName, SCHEMA_INDEX_QUERY_SQL, attribute, count); + + return (count == 1); +} +/** + * @tc.name: SchemaIndex 001 + * @tc.desc: Verify that the schema db created with non-index mode can be upgrade to index mode when open it again with + * schema that include schema_index. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaIndex001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that without schema_index and close but don't delete it. + * @tc.expected: step1. operate successfully. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step2. open the db again with schema_index mode, and the schema_index is + * [$.field9,$.field10.field10,$._field1.field3.field4,$._field1.field3.field5.field6]. + * @tc.expected: step2. open successfully. + */ + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4); + option.createIfNecessary = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step3. check is the schema_index exist. + * @tc.expected: step3. can find 4 schema_indexes in schema table. + */ + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field10.field10")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field4")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field5.field6")); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SchemaIndex 002 + * @tc.desc: Verify that if the schema db created with index mode, then it can be degrade to non-index mode db + * when open it again with schema that do not include schema_index. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaIndex002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that with schema_index. + * @tc.expected: step1. create successfully. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + /** + * @tc.steps: step2. check is the schema_index exist. + * @tc.expected: step2. can find 4 schema_indexes in schema table. + */ + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field10.field10")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field4")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field5.field6")); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step3. open the db again with non_index mode. + * @tc.expected: step3. open successfully. + */ + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1); + option.createIfNecessary = false; + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + /** + * @tc.steps: step4. check is the schema_index exist. + * @tc.expected: step4. can't find the schema_index in schema table. + */ + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field10.field10")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field4")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field5.field6")); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +void CloseKvStoreTemp(KvStoreDelegateManager *&manager, KvStoreNbDelegate *&delegate) +{ + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + delete manager; + manager = nullptr; +} +/** + * @tc.name: SchemaIndex 003 + * @tc.desc: Verify that whether the schema db has schema_index depended on is the first delegate has schema_index + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaIndex003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that without schema_index and close it but do not delete it. + * @tc.expected: step1. create and close successfully. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step2. open the db again with index mode, the schema_index is VALID_INDEX_4. + * @tc.expected: step2. open successfully. + */ + KvStoreNbDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4); + option.createIfNecessary = false; + delegate1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter2, option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + + /** + * @tc.steps: step3. open the db again with non-index mode. + * @tc.expected: step3. open failed and return SCHEMA_MISMATCH. + */ + KvStoreNbDelegate *delegate2 = nullptr; + KvStoreDelegateManager *manager2 = nullptr; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1); + DBStatus status; + delegate2 = DistributedDBNbTestTools::GetNbDelegateStatus(manager2, status, g_dbParameter2, option); + ASSERT_EQ(delegate2, nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. open the db again using index mode with the index that is different from the index which used + * by the first delegate. + * @tc.expected: step4. open failed and return SCHEMA_MISMATCH. + */ + KvStoreNbDelegate *delegate3 = nullptr; + KvStoreDelegateManager *manager3 = nullptr; + std::string validIndexTemp = "[\"$.field9\", \"$._field1.field3.field5.field6\"]"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, validIndexTemp); + delegate3 = DistributedDBNbTestTools::GetNbDelegateStatus(manager3, status, g_dbParameter2, option); + ASSERT_EQ(delegate3, nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step5. open the db again using index mode with the index that is used by the first delegate. + * @tc.expected: step5. open successfully. + */ + KvStoreNbDelegate *delegate4 = nullptr; + KvStoreDelegateManager *manager4 = nullptr; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4); + delegate4 = DistributedDBNbTestTools::GetNbDelegateStatus(manager4, status, g_dbParameter2, option); + ASSERT_NE(delegate4, nullptr); + EXPECT_EQ(status, OK); + + CloseKvStoreTemp(manager1, delegate1); + CloseKvStoreTemp(manager4, delegate4); + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} + +/** + * @tc.name: SchemaIndex 004 + * @tc.desc: Verify that if open the schema db has schema_index circularly using wrong schema index and non-index will + * remove the valid index + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaIndex004, TestSize.Level2) +{ + /** + * @tc.steps: step1. create schema db that with schema_index and close it but do not delete it. + * @tc.expected: step1. create and close successfully. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step2. check is the schema_index exist. + * @tc.expected: step2. can find 4 schema_indexes in schema table. + */ + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field10.field10")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field4")); + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field5.field6")); + + /** + * @tc.steps: step3. open the db with the different index and non-index circularly. + * @tc.expected: step3. open success. + */ + std::string validIndexTemp = "[\"$.field9\", \"$._field1.field3.field5.field6\"]"; + KvStoreNbDelegate *delegate1 = nullptr; + KvStoreDelegateManager *manager1 = nullptr; + option.createIfNecessary = false; + for (int index =0; index < HUNDRED_TIMES; index++) { + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, validIndexTemp); + delegate1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter2, option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + CloseKvStoreTemp(manager1, delegate1); + + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1); + delegate1 = DistributedDBNbTestTools::GetNbDelegateSuccess(manager1, g_dbParameter2, option); + ASSERT_TRUE(manager1 != nullptr && delegate1 != nullptr); + CloseKvStoreTemp(manager1, delegate1); + } + + /** + * @tc.steps: step4. check is the schema_index exist. + * @tc.expected: step4. can't find the schema_index in schema table. + */ + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field10.field10")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field4")); + EXPECT_FALSE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$._field1.field3.field5.field6")); + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} + +void RandIndex(bool isFirst) +{ + std::string validIndexTemp; + if (isFirst) { + validIndexTemp = validIndexTemp + "[\"$.field9\", \"$._field1.field3.field4\"]"; + } else { + validIndexTemp = validIndexTemp + "[\"$.field9\", \"$._field1.field3.field5.field6\"]"; + } + std::string validIndexTemp2 = "[\"$.field9\"]"; + vector indexes = {validIndexTemp, validIndexTemp2}; + int randSubscript = GetRandInt(0, 1); + + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, indexes[randSubscript]); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + if (delegate != nullptr) { + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + } + delete manager; + manager = nullptr; +} + +/** + * @tc.name: SchemaIndex 005 + * @tc.desc: Verify that Concurrency operate index won't cause the process to crash + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SchemaIndex005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that with index $.field9 and close it but do not delete it. + * @tc.expected: step1. create and close successfully. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + std::string validIndexTemp = "[\"$.field9\"]"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, validIndexTemp); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + + /** + * @tc.steps: step2. check is the schema_index exist. + * @tc.expected: step2. can find 1 schema_index in schema table. + */ + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + + /** + * @tc.steps: step3. start 5 threads to increase or decrease index field, 3 of which randly increase or decrease + * [$._field1.field3.field4] and the rest of them increase or decrease [$._field1.field3.field5.field6] randly. + * @tc.expected: step3. the program runs normally. + */ + vector threads; + threads.push_back(std::thread(RandIndex, true)); + threads.push_back(std::thread(RandIndex, true)); + threads.push_back(std::thread(RandIndex, true)); + threads.push_back(std::thread(RandIndex, false)); + threads.push_back(std::thread(RandIndex, false)); + + for (auto &th : threads) { + th.join(); + } + + /** + * @tc.steps: step4. check is the schema_index exist. + * @tc.expected: step4. can find the index $.field9 in schema table. + */ + EXPECT_TRUE(SchemaIndexQuery(g_dbParameter2, NB_DIRECTOR, "$.field9")); + + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; +} + +/** + * @tc.name: SkipTest 001 + * @tc.desc: Verify that SCHEMA_SKIPSIZE must be integer in range [0, 4M-2] or it will return INVALID_SCHEMA + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SkipTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that with invalid skip size "10.5", "true", "*&", "汉字", "-10", "4194305". + * @tc.expected: step1. create failed and return INVALID_SCHEMA. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + vector skipSize = {"10.5", "\"20\"", "true", "-10", "*&", "汉字", "4194303", "4194304"}; + for (unsigned long index = 0; index < skipSize.size(); index++) { + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize[index]); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_EQ(delegate, nullptr); + EXPECT_EQ(status, INVALID_SCHEMA); + } + + /** + * @tc.steps: step2. create schema db that with invalid skip size "10.0", "1.23e2", "4194303", "4194304". + * @tc.expected: step2. create failed and return INVALID_SCHEMA. + */ + skipSize.clear(); + skipSize = {"0", "10", "123", "4194302"}; + for (unsigned long index = 0; index < skipSize.size(); index++) { + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize[index]); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(status, OK); + EXPECT_EQ(manager->CloseKvStore(delegate), OK); + delegate = nullptr; + EXPECT_EQ(manager->DeleteKvStore(STORE_ID_2), OK); + delete manager; + manager = nullptr; + } +} + +/** + * @tc.name: SkipTest 002 + * @tc.desc: Verify that the value must be with prefix whose size is SCHEMA_SKIPSIZE or it will put failed. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SkipTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with valid skip size "16". + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + std::string skipSize = "16"; + const std::string validDefine = "{\"field1\":\"STRING,NOT NULL,DEFAULT 'fxy'\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, validDefine, VALID_INDEX_1, skipSize); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. put (k1, v1) to db, v1.size() < 16, put (k2, v2) to db, v2.size() > 16 but has no prefix. + * @tc.expected: step2. put failed. + */ + std::string schemaValue = "{\"field1\":\"fxy\"}"; + + Value value1; + value1.assign(15, 'a'); // 15 bytes char, which is shorter than 16 + std::string schemaValueMiddle = "aa{\"field1\":\"fxy\"}"; + Value value2(schemaValueMiddle.begin(), schemaValueMiddle.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), INVALID_FORMAT); + EXPECT_EQ(delegate->Put(KEY_2, value2), INVALID_FORMAT); + /** + * @tc.steps: step3. put 3 valid records which has the same schema define. + * @tc.expected: step3. create failed and return INVALID_SCHEMA. + */ + string validPre1 = "abbbbabbbbabbbbb"; + string validPre2 = "abbbbabbbbabbbbc"; + string validPre3 = "abbbbabbbbabbbbd"; + + std::string defineValue1 = validPre1 + schemaValue; + std::string defineValue2 = validPre2 + schemaValue; + std::string defineValue3 = validPre3 + schemaValue; + Value value4(defineValue1.begin(), defineValue1.end()); + Value value5(defineValue2.begin(), defineValue2.end()); + Value value6(defineValue3.begin(), defineValue3.end()); + EXPECT_EQ(delegate->Put(KEY_4, value4), OK); + EXPECT_EQ(delegate->Put(KEY_5, value5), OK); + EXPECT_EQ(delegate->Put(KEY_6, value6), OK); + + /** + * @tc.steps: step4. query the result in the db on field = "fxy". + * @tc.expected: step4. can find 3 records in the db. + */ + vector entries; + Query query = Query::Select().EqualTo("$.field1", "fxy"); + EXPECT_EQ(delegate->GetEntries(query, entries), OK); + EXPECT_EQ(entries.size(), static_cast(3)); // 3 record + + /** + * @tc.steps: step5. delete k3, and query the result in the db on field = "fxy". + * @tc.expected: step5. can find 2 records in the db. + */ + EXPECT_EQ(delegate->Delete(KEY_6), OK); + query = Query::Select().EqualTo("$.field1", "fxy"); + EXPECT_EQ(delegate->GetEntries(query, entries), OK); + vector entriesExpect = {{KEY_4, value4}, {KEY_5, value5}}; + EXPECT_TRUE(CompareEntriesVector(entries, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SkipTest 003 + * @tc.desc: Verify that when the field in pre-SCHEMA_SKIPSIZE, it will be ignored. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SkipTest003, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with valid skip size "16". + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + std::string skipSize = "16"; + const std::string validDefine = "{\"field1\":\"STRING,NOT NULL,DEFAULT 'fxy'\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, validDefine, VALID_INDEX_1, skipSize); + delegate = DistributedDBNbTestTools::GetNbDelegateStatus(manager, status, g_dbParameter2, option); + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step2. put (k1, v1) to db, which has field2 in the prefix char only. + * @tc.expected: step2. put successfully. + */ + string validPre1 = "{\"field1\":\"abc\"}{\"field1\":\"fxy\"}"; + string validPre2 = "{\"field1\":\"abc\"}{\"field1\":\"fxy\", \"field2\": \"abc\"}"; + + Value value1(validPre1.begin(), validPre1.end()); + Value value2(validPre2.begin(), validPre2.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), OK); + + /** + * @tc.steps: step3. query the result in the db on field2 = "fxy". + * @tc.expected: step3. can't find records in the db. + */ + vector entries; + Query query = Query::Select().EqualTo("$.field1", "abc"); + EXPECT_EQ(delegate->GetEntries(query, entries), NOT_FOUND); + + /** + * @tc.steps: step4. put (k2, v2) to db and query the records in db on field2 = "fxy". + * @tc.expected: step4. can find one record in the db. + */ + EXPECT_EQ(delegate->Put(KEY_2, value2), OK); + query = Query::Select().EqualTo("$.field1", "fxy"); + EXPECT_EQ(delegate->GetEntries(query, entries), OK); + vector entriesExpect = {{KEY_1, value1}, {KEY_2, value2}}; + EXPECT_TRUE(CompareEntriesVector(entries, entriesExpect)); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: SkipTest 004 + * @tc.desc: Verify that when open the db that has a valid schema-size with a schema-size that not equal to define, + * or without schema-size, it will open failed and return SCHEMA_MISMATCH + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SkipTest004, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that with valid skip size "16". + * @tc.expected: step1. create success. + */ + map delegate; + map manager; + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + std::string skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize); + delegate["ValidSkip"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["ValidSkip"], + status, g_dbParameter2, option); + EXPECT_NE(delegate["ValidSkip"], nullptr); + EXPECT_EQ(status, OK); + EXPECT_EQ(manager["ValidSkip"]->CloseKvStore(delegate["ValidSkip"]), OK); + delegate["ValidSkip"] = nullptr; + delete manager["ValidSkip"]; + manager["ValidSkip"] = nullptr; + + /** + * @tc.steps: step2. open the db that without SCHEMA_SIZE. + * @tc.expected: step2. open failed and return SCHEMA_MISMATCH. + */ + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1); + delegate["NoSkip"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["NoSkip"], + status, g_dbParameter2, option); + EXPECT_EQ(delegate["NoSkip"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + /** + * @tc.steps: step3. open the db again with the SCHEMA_SIZE that larger or smaller than skipSize. + * @tc.expected: step3. open failed and return SCHEMA_MISMATCH. + */ + skipSize = "17"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize); + delegate["longerSkip"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["longerSkip"], + status, g_dbParameter2, option); + EXPECT_EQ(delegate["longerSkip"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + skipSize = "15"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize); + delegate["shorterSkip"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["shorterSkip"], + status, g_dbParameter2, option); + EXPECT_EQ(delegate["shorterSkip"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step4. open the db the right SCHEMA_SIZE. + * @tc.expected: step4. open successfully. + */ + skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_4, skipSize); + delegate["ValidSkip"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["ValidSkip"], + status, g_dbParameter2, option); + EXPECT_NE(delegate["ValidSkip"], nullptr); + EXPECT_EQ(manager["ValidSkip"]->CloseKvStore(delegate["ValidSkip"]), OK); + delegate["ValidSkip"] = nullptr; + EXPECT_EQ(status, OK); + + EXPECT_EQ(manager["ValidSkip"]->DeleteKvStore(STORE_ID_2), OK); + delete manager["ValidSkip"]; + manager["ValidSkip"] = nullptr; +} + +/** + * @tc.name: SkipTest 005 + * @tc.desc: Verify that it can open one db that with schema-size with a schema that include schema-size. + * And also, it will must need the right schema-size that used the first time to open the db after that. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, SkipTest005, TestSize.Level1) +{ + /** + * @tc.steps: step1. create schema db that without schema. + * @tc.expected: step1. create success. + */ + map delegate; + mapmanager; + Option option = g_option; + option.isMemoryDb = false; + DBStatus status; + delegate["Normal"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["Normal"], + status, g_dbParameter2, option); + ASSERT_NE(delegate["Normal"], nullptr); + EXPECT_EQ(status, OK); + EXPECT_EQ(manager["Normal"]->CloseKvStore(delegate["Normal"]), OK); + delegate["Normal"] = nullptr; + + /** + * @tc.steps: step2. open the db again with the schema and SCHEMA_SIZE = 16. + * @tc.expected: step2. open successfully. + */ + std::string skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_3, skipSize); + delegate["Skip1"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["Skip1"], status, g_dbParameter2, option); + ASSERT_NE(delegate["Skip1"], nullptr); + EXPECT_EQ(status, OK); + + /** + * @tc.steps: step3. open the db again with the SCHEMA_SIZE = 15. + * @tc.expected: step3. open failed and return SCHEMA_MISMATCH. + */ + skipSize = "15"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_1, skipSize); + delegate["Skip2"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["Skip2"], status, g_dbParameter2, option); + ASSERT_EQ(delegate["Skip2"], nullptr); + EXPECT_EQ(status, SCHEMA_MISMATCH); + + /** + * @tc.steps: step3. open the db that without SCHEMA_SIZE. + * @tc.expected: step3. open failed and return SCHEMA_MISMATCH. + */ + skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_2, VALID_DEFINE_1, VALID_INDEX_3, skipSize); + delegate["Skip2"] = DistributedDBNbTestTools::GetNbDelegateStatus(manager["Skip2"], status, g_dbParameter2, option); + ASSERT_NE(delegate["Skip2"], nullptr); + EXPECT_EQ(status, OK); + EXPECT_EQ(manager["Skip2"]->CloseKvStore(delegate["Skip2"]), OK); + delegate["Skip2"] = nullptr; + delete manager["Skip2"]; + manager["Skip2"] = nullptr; + + EXPECT_EQ(manager["Skip1"]->CloseKvStore(delegate["Skip1"]), OK); + delegate["Skip1"] = nullptr; + delete manager["Skip1"]; + manager["Skip1"] = nullptr; + EXPECT_EQ(manager["Normal"]->DeleteKvStore(STORE_ID_2), OK); + delete manager["Normal"]; + manager["Normal"] = nullptr; +} + +/** + * @tc.name: DataTypeVerify 001 + * @tc.desc: Verify that it can Verify the data type of INTEGER and LONG criticality value rightly. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, DataTypeVerify001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with schema include the field of INTEGER LONG and DOUBLE type. + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + std::string schemaDefine = "{\"field1\":\"INTEGER ,DEFAULT null\",\"field2\":\"LONG ,DEFAULT null\"," + "\"field3\":\"DOUBLE ,DEFAULT null\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step2. put the records where field1 is INT32_MIN, INT32_MAX, INT32_MIN - 1, and INT32_MAX + 1. + * @tc.expected: step2. when field1 is INT32_MIN, INT32_MAX can put successfully, + * but when it is INT32_MIN - 1, and INT32_MAX + 1 will be failed. + */ + string int32Min = "{\"field1\":-2147483648,\"field2\":null, \"field3\":null}"; + string int32Max = "{\"field1\":2147483647,\"field2\":null, \"field3\":null}"; + string int32MinSub1 = "{\"field1\":-2147483649,\"field2\":null, \"field3\":null}"; + string int32MaxPlus1 = "{\"field1\":2147483648,\"field2\":null, \"field3\":null}"; + Value value1(int32Min.begin(), int32Min.end()); + Value value2(int32Max.begin(), int32Max.end()); + Value value3(int32MinSub1.begin(), int32MinSub1.end()); + Value value4(int32MaxPlus1.begin(), int32MaxPlus1.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), OK); + EXPECT_EQ(delegate->Put(KEY_2, value2), OK); + EXPECT_EQ(delegate->Put(KEY_3, value3), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_4, value4), INVALID_FIELD_TYPE); + + /** + * @tc.steps: step3. put the records where field2 is INT64_MIN, INT64_MAX, INT64_MIN - 1, and INT64_MAX + 1. + * @tc.expected: step3. when field2 is INT64_MIN, INT64_MAX can put successfully, + * but when it is INT64_MIN - 1, and INT64_MAX + 1 will be failed. + */ + string int64Min = "{\"field1\":null,\"field2\":-9223372036854775808, \"field3\":null}"; + string int64Max = "{\"field1\":null,\"field2\":9223372036854775807, \"field3\":null}"; + string int64MinSub1 = "{\"field1\":null,\"field2\":-9223372036854775809, \"field3\":null}"; + string int64MaxPlus1 = "{\"field1\":null,\"field2\":9223372036854775808, \"field3\":null}"; + Value value5(int64Min.begin(), int64Min.end()); + Value value6(int64Max.begin(), int64Max.end()); + Value value7(int64MinSub1.begin(), int64MinSub1.end()); + Value value8(int64MaxPlus1.begin(), int64MaxPlus1.end()); + EXPECT_EQ(delegate->Put(KEY_1, value5), OK); + EXPECT_EQ(delegate->Put(KEY_2, value6), OK); + EXPECT_EQ(delegate->Put(KEY_3, value7), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_4, value8), INVALID_FIELD_TYPE); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: DataTypeVerify 002 + * @tc.desc: Verify that it can Verify the data type of INTEGER and LONG criticality value rightly. + * @tc.type: FUNC + * @tc.require: SR000DR9JQ + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, DataTypeVerify002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with schema include the field of INTEGER LONG and DOUBLE type. + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + std::string schemaDefine = "{\"field1\":\"INTEGER ,DEFAULT null\",\"field2\":\"LONG ,DEFAULT null\"," + "\"field3\":\"DOUBLE ,DEFAULT null\"}"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, VALID_INDEX_1); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step2. put the records where field1 is -10.1, 10.1, 10E5, and 10.123E5. + * @tc.expected: step2. all of them will be failed. + */ + string negativeAndPoitInt = "{\"field1\":-10.1,\"field2\":null, \"field3\":null}"; + string positiveAndPoitInt = "{\"field1\":10.1,\"field2\":null, \"field3\":null}"; + string scientificNotationInt = "{\"field1\":10E5,\"field2\":null, \"field3\":null}"; + string scientificNotationAndPoitInt = "{\"field1\":10.123E5,\"field2\":null, \"field3\":null}"; + Value value1(negativeAndPoitInt.begin(), negativeAndPoitInt.end()); + Value value2(positiveAndPoitInt.begin(), positiveAndPoitInt.end()); + Value value3(scientificNotationInt.begin(), scientificNotationInt.end()); + Value value4(scientificNotationAndPoitInt.begin(), scientificNotationAndPoitInt.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_2, value2), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_3, value3), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_4, value4), INVALID_FIELD_TYPE); + + /** + * @tc.steps: step3. put the records where field2 is -10.1, 10.1, 10E5, and 10.123E5. + * @tc.expected: step3. all of them will be failed. + */ + string negativeAndPoitLong = "{\"field1\":null,\"field2\":-10.1, \"field3\":null}"; + string positiveAndPoitLong = "{\"field1\":null,\"field2\":10.1, \"field3\":null}"; + string scientificNotationLong = "{\"field1\":null,\"field2\":10E5, \"field3\":null}"; + string scientificNotationAndPoitLong = "{\"field1\":null,\"field2\":10.123E5, \"field3\":null}"; + Value value5(negativeAndPoitLong.begin(), negativeAndPoitLong.end()); + Value value6(positiveAndPoitLong.begin(), positiveAndPoitLong.end()); + Value value7(scientificNotationLong.begin(), scientificNotationLong.end()); + Value value8(scientificNotationAndPoitLong.begin(), scientificNotationAndPoitLong.end()); + EXPECT_EQ(delegate->Put(KEY_1, value5), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_2, value6), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_3, value7), INVALID_FIELD_TYPE); + EXPECT_EQ(delegate->Put(KEY_4, value8), INVALID_FIELD_TYPE); + + /** + * @tc.steps: step4. put the records where field3 is -10.1, 10.1, 10E5, and 10.123E5. + * @tc.expected: step4. all of them will be success. + */ + string negativeAndPoitDouble = "{\"field1\":null,\"field2\":null, \"field3\":-10.1}"; + string positiveAndPoitDouble = "{\"field1\":null,\"field2\":null, \"field3\":10.1}"; + string scientificNotationDouble = "{\"field1\":null,\"field2\":null, \"field3\":10E5}"; + string scientificNotationAndPoitDouble = "{\"field1\":null,\"field2\":null, \"field3\":10.123E5}"; + Value value9(negativeAndPoitDouble.begin(), negativeAndPoitDouble.end()); + Value value10(positiveAndPoitDouble.begin(), positiveAndPoitDouble.end()); + Value value11(scientificNotationDouble.begin(), scientificNotationDouble.end()); + Value value12(scientificNotationAndPoitDouble.begin(), scientificNotationAndPoitDouble.end()); + EXPECT_EQ(delegate->Put(KEY_1, value9), OK); + EXPECT_EQ(delegate->Put(KEY_2, value10), OK); + EXPECT_EQ(delegate->Put(KEY_3, value11), OK); + EXPECT_EQ(delegate->Put(KEY_4, value12), OK); + + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} +namespace { +std::string GenerateLargeDepth(int depth) +{ + string resultStr; + for (int index = 1; index <= depth; index++) { + std::string ind = std::to_string(index); + if (index == depth) { + resultStr = resultStr + "\"filed" + ind + "\":" + ind; + } else { + resultStr = resultStr + "\"filed" + ind + "\":{"; + } + } + resultStr.append(depth - 1, '}'); + return resultStr; +} +const int SKIPSTR_LEN = 16; +const int JSON_LEN = 99; +} + +/** + * @tc.name: JsonTest 001 + * @tc.desc: Verify that it can parse the Json string which nesting depth is equal to 100. + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, JsonTest001, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with schema_define = {"field":{}, "field1":"STRING"}. + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + std::string schemaDefine = "{\"field\":{}, \"field1\": \"STRING\"}"; + string schemaIndex = "[\"$.field1\"]"; + std::string skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, schemaIndex, skipSize); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step2. put the records where the depth of Json is equal to 100. + * @tc.expected: step2. put successfully. + */ + string skipStr(SKIPSTR_LEN, '0'); + string field1Str = skipStr + "{\"field\":" + "{\"field1\": \"string\"}" + ",\"field1\":\"abc\"}"; + string field1Int = skipStr + "{\"field\":" + "{\"field1\": 0}" + ",\"field1\":\"bcd\"}"; + string field1Long = skipStr + "{\"field\":" + "{\"field1\": 500}" + ",\"field1\":\"cde\"}"; + string field1Float = skipStr + "{\"field\":" + "{\"field1\": 3.14}" + ",\"field1\":\"def\"}"; + string field1 = GenerateLargeDepth(JSON_LEN); + string field1Object = skipStr + "{\"field\":{" + field1 + "},\"field1\":\"efg\"}"; + Value value1(field1Str.begin(), field1Str.end()); + Value value2(field1Int.begin(), field1Int.end()); + Value value3(field1Long.begin(), field1Long.end()); + Value value4(field1Float.begin(), field1Float.end()); + Value value5(field1Object.begin(), field1Object.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), OK); + EXPECT_EQ(delegate->Put(KEY_2, value2), OK); + EXPECT_EQ(delegate->Put(KEY_3, value3), OK); + EXPECT_EQ(delegate->Put(KEY_4, value4), OK); + EXPECT_EQ(delegate->Put(KEY_5, value5), OK); + + /** + * @tc.steps: step3. query data in db. + * @tc.expected: step3. operate successfully. + */ + vector entriesResult; + KvStoreResultSet *resultSet = nullptr; + Query query = Query::Select().PrefixKey({'k'}).OrderBy("$.field1", true); + EXPECT_EQ(delegate->GetEntries(query, entriesResult), OK); + EXPECT_EQ(delegate->GetEntries(query, resultSet), OK); + EXPECT_TRUE(entriesResult.size() == FIVE_RECORDS); + EXPECT_TRUE(resultSet->GetCount() == FIVE_RECORDS); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} + +/** + * @tc.name: JsonTest 002 + * @tc.desc: Verify that it can parse the Json string which include "\n,\r,\t". + * @tc.type: FUNC + * @tc.require: SR000F3L0Q + * @tc.author: fengxiaoyun + */ +HWTEST_F(DistributeddbNbSchemaDbTest, JsonTest002, TestSize.Level0) +{ + /** + * @tc.steps: step1. create schema db that with schema_define = {"field":{}, "field1":"STRING"}. + * @tc.expected: step1. create success. + */ + KvStoreNbDelegate *delegate = nullptr; + KvStoreDelegateManager *manager = nullptr; + Option option = g_option; + option.isMemoryDb = false; + std::string schemaDefine = "{\"field\":{}, \"field1\": \"STRING\"}"; + std::string skipSize = "16"; + option.schema = SpliceToSchema(VALID_VERSION_1, VALID_MODE_1, schemaDefine, VALID_INDEX_1, skipSize); + delegate = DistributedDBNbTestTools::GetNbDelegateSuccess(manager, g_dbParameter2, option); + ASSERT_TRUE(manager != nullptr && delegate != nullptr); + + /** + * @tc.steps: step2. put the records where the Json string includes "\n,\r,\t". + * @tc.expected: step2. put successfully. + */ + string skipStr(SKIPSTR_LEN, '\\'); + string field1Str = skipStr + "{\"field\":" + "{\"\\nfield1\": \"a\"}" + ",\"field1\":\"abc\"}"; + string fieldBool = skipStr + "{\"field\":" + "{\"\\rfield1\": true}" + ",\"field1\":\"bcd\"}"; + // test Json string with space. + string field1Int = skipStr + "{\"field\":" + "{\"field1\": 100}" + ",\"field1\":\"cde\"}"; + string field1Long = skipStr + "{\"field\":" + "{\"field1\": 1000}" + ",\"field1\":\"def\\t\"}"; + string field1Float = skipStr + "{\"field\":" + "{\"field1\": 3.14}" + ",\"field1\":\"efg\"}"; + Value value1(field1Str.begin(), field1Str.end()); + Value value2(fieldBool.begin(), fieldBool.end()); + Value value3(field1Int.begin(), field1Int.end()); + Value value4(field1Long.begin(), field1Long.end()); + Value value5(field1Float.begin(), field1Float.end()); + EXPECT_EQ(delegate->Put(KEY_1, value1), OK); + EXPECT_EQ(delegate->Put(KEY_2, value2), OK); + EXPECT_EQ(delegate->Put(KEY_3, value3), OK); + EXPECT_EQ(delegate->Put(KEY_4, value4), OK); + EXPECT_EQ(delegate->Put(KEY_5, value5), OK); + + /** + * @tc.steps: step3. query data in db. + * @tc.expected: step3. operate successfully. + */ + vector entriesResult; + KvStoreResultSet *resultSet = nullptr; + Query query = Query::Select().IsNotNull("$.field1").Limit(10, 0); // query from 0 offset 10. + EXPECT_EQ(delegate->GetEntries(query, entriesResult), OK); + EXPECT_EQ(delegate->GetEntries(query, resultSet), OK); + EXPECT_TRUE(entriesResult.size() == FIVE_RECORDS); + EXPECT_TRUE(resultSet->GetCount() == FIVE_RECORDS); + EXPECT_EQ(delegate->CloseResultSet(resultSet), OK); + EXPECT_TRUE(EndCaseDeleteDB(manager, delegate, STORE_ID_2, option.isMemoryDb)); +} +} +#endif \ No newline at end of file diff --git a/test/resource/distributeddataservice/config/trans_permission.json b/test/resource/distributeddataservice/config/trans_permission.json new file mode 100755 index 000000000..fd9b0dcd5 --- /dev/null +++ b/test/resource/distributeddataservice/config/trans_permission.json @@ -0,0 +1,130 @@ +[ + { + "SESSION_NAME": "ohos\\.distributedschedule\\.foundation.*", + "REGEXP": "true", + "DEVID": "UUID", + "APP_INFO": [ + { + "TYPE": "system_app", + "UID": "1000", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "com\\.huawei\\.def.*", + "REGEXP": "true", + "DEVID": "NETWORKID", + "APP_INFO": [ + { + "TYPE": "system_app", + "UID": "1000", + "PKG_NAME": "com.huawei.def", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "com\\.huawei\\.hwddmp.*", + "REGEXP": "true", + "DEVID": "UUID", + "APP_INFO": [ + { + "TYPE": "system_app", + "UID": "1000", + "PKG_NAME": "com.huawei.hwddmp", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "com.huawei.devicegroupmanage", + "DEVID": "UDID", + "APP_INFO": [ + { + "TYPE": "system_app", + "PKG_NAME": "com.huawei.devicegroupmanage", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "DBinderService", + "DEVID": "NETWORKID", + "APP_INFO": [ + { + "TYPE": "native_app", + "UID": "1000", + "PKG_NAME": "DBinderService", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "DBinder.*", + "REGEXP": "true", + "DEVID": "NETWORKID", + "SEC_LEVEL": "public", + "APP_INFO": [ + { + "TYPE": "granted_app", + "PKG_NAME": "DBinderBus", + "ACTIONS": "open" + } + ] + }, + { + "SESSION_NAME": "distributeddata.*", + "REGEXP": "true", + "DEVID": "UUID", + "APP_INFO": [ + { + "TYPE": "system_app", + "UID": "1000", + "PKG_NAME": "com.huawei.hwddmp", + "ACTIONS": "create,open" + }, + { + "TYPE": "system_app", + "UID": "0", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "SystemAbilityManager_DDC.*", + "REGEXP": "true", + "DEVID": "UUID", + "APP_INFO": [ + { + "TYPE": "native_app", + "UID": "1000", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "hiview_distributed_network_softbus", + "DEVID": "NETWORKID", + "APP_INFO": [ + { + "TYPE": "native_app", + "UID": "0", + "PKG_NAME": "hiview_distributed_network_softbus", + "ACTIONS": "create,open" + } + ] + }, + { + "SESSION_NAME": "DistributedFileService.*", + "REGEXP": "true", + "DEVID": "UUID", + "APP_INFO": [ + { + "TYPE": "native_app", + "UID": "1000", + "ACTIONS": "create,open" + } + ] + } +] \ No newline at end of file diff --git a/test/resource/distributeddataservice/ohos_test.xml b/test/resource/distributeddataservice/ohos_test.xml new file mode 100755 index 000000000..e89ac8c42 --- /dev/null +++ b/test/resource/distributeddataservice/ohos_test.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/test/resource/distributeddb/baseline.xml b/test/resource/distributeddb/baseline.xml new file mode 100755 index 000000000..a8b2364c9 --- /dev/null +++ b/test/resource/distributeddb/baseline.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test/resource/distributeddb/ohos_test.xml b/test/resource/distributeddb/ohos_test.xml new file mode 100755 index 000000000..21fc76dca --- /dev/null +++ b/test/resource/distributeddb/ohos_test.xml @@ -0,0 +1,22 @@ + + + + + + + +