diff --git a/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h b/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h index 377551c8c98491f9fcf9705d8568a85582cef63f..d4981de8c43530f684442a52a846cca744e17f75 100644 --- a/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h +++ b/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h @@ -104,8 +104,16 @@ public: static constexpr const uint32_t ON_CHANGE_CLOUD = 0x8; static constexpr std::chrono::milliseconds DFX_TIME_THRESHOLD = std::chrono::milliseconds(1000); + + static constexpr std::chrono::milliseconds ASYNC_GEN_LOG_INTERVAL = std::chrono::milliseconds(20); static constexpr std::chrono::milliseconds LONG_TIME_TRANSACTION = std::chrono::milliseconds(1000); static constexpr std::chrono::milliseconds LONG_TRANSACTION_INTERVAL = std::chrono::milliseconds(50); + + // change local flag to :keep async download asset[0x1000] + // mark local last write[0x02] + // mark device cloud inconsistency[0x20] + // mark not cloud update local[~0x4000] + static constexpr const char *LOCAL_UPDATE_FLAG = "((flag&0x1000)|0x02|0x20)&~0x4000"; }; } // namespace DistributedDB #endif // CLOUD_DB_CONSTANT_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/common/include/db_common.h b/frameworks/libs/distributeddb/common/include/db_common.h index d13ee818b2cc1b9ec6075b07012f6c85557f7423..1d3484862bad4769ebd02a4c5f554782d1b9e7e9 100644 --- a/frameworks/libs/distributeddb/common/include/db_common.h +++ b/frameworks/libs/distributeddb/common/include/db_common.h @@ -177,6 +177,8 @@ public: bool allowStoreIdWithDot); static uint32_t TransfDbVersionToSoftwareVersion(uint16_t dbVersion); + + static bool IsStringAllDigit(const std::string &originStr); private: static void InsertNodesByScore(const std::map> &graph, const std::vector &generateNodes, const std::map &scoreGraph, diff --git a/frameworks/libs/distributeddb/common/include/db_errno.h b/frameworks/libs/distributeddb/common/include/db_errno.h index 371f8f918a4e2182ba92fc08dc80c968522ce7d4..793913ae9230e1eb7eb8e972827c075d606ef401 100644 --- a/frameworks/libs/distributeddb/common/include/db_errno.h +++ b/frameworks/libs/distributeddb/common/include/db_errno.h @@ -197,6 +197,7 @@ constexpr const int E_CLOUD_ASSET_NOT_FOUND = (E_BASE + 210); // Cloud download constexpr const int E_TABLE_NOT_FOUND = (E_BASE + 211); constexpr const int E_SKIP_WHEN_CLOUD_SPACE_INSUFFICIENT = (E_BASE + 212); // Upload failed by cloud space insufficient constexpr const int E_EXPIRED_CURSOR = (E_BASE + 213); // Cursor invalid in cloud +constexpr const int E_TASK_INTERRUPTED = (E_BASE + 214); // Task(cloud sync, generate log) interrupted } // namespace DistributedDB #endif // DISTRIBUTEDDB_ERRNO_H diff --git a/frameworks/libs/distributeddb/common/include/relational/table_info.h b/frameworks/libs/distributeddb/common/include/relational/table_info.h index f28404072921cf62234cc044d15fd8a858196ee3..f01bd497d5dc359a6baac9792d65e8a0c532ab4c 100644 --- a/frameworks/libs/distributeddb/common/include/relational/table_info.h +++ b/frameworks/libs/distributeddb/common/include/relational/table_info.h @@ -16,6 +16,7 @@ #define TABLE_INFO_H #include +#include #include #include @@ -147,6 +148,14 @@ public: std::vector RemovePKCompositeFields(const std::vector &uniqueFields) const; + void SetCloudTable(const std::optional &table); + + std::vector GetCloudSyncDistributedPk() const; + + std::vector GetCloudSyncFields() const; + + std::optional GetCloudTable() const; + void SetType(const std::string &type); bool IsView() const; @@ -182,6 +191,7 @@ private: // b c // d e f ,table_info[a] = {b,c} [b] = {d, e} [c] = {f} std::vector sourceTableReferenced_; + std::optional cloudTable_; std::string type_; }; } // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/common/src/db_common_client.cpp b/frameworks/libs/distributeddb/common/src/db_common_client.cpp index 5d54fa98e8e1e718785a503ea15d4fea94c7dbd9..fa103ec9fe8efac192436a76f6e299184403367f 100644 --- a/frameworks/libs/distributeddb/common/src/db_common_client.cpp +++ b/frameworks/libs/distributeddb/common/src/db_common_client.cpp @@ -184,4 +184,17 @@ bool DBCommon::CaseInsensitiveCompare(const std::string &first, const std::strin { return (strcasecmp(first.c_str(), second.c_str()) == 0); } + +bool DBCommon::IsStringAllDigit(const std::string &originStr) +{ + if (originStr.empty()) { + return false; + } + for (const auto &item : originStr) { + if (item < '0' || item > '9') { + return false; + } + } + return true; +} } // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/common/src/query_expression.cpp b/frameworks/libs/distributeddb/common/src/query_expression.cpp index a6b01f47414c36b6465b6e9af2c8c224f038d9d0..1cdf28a27d8192189f5192070db90da764830df6 100644 --- a/frameworks/libs/distributeddb/common/src/query_expression.cpp +++ b/frameworks/libs/distributeddb/common/src/query_expression.cpp @@ -508,8 +508,10 @@ void QueryExpression::SetNotSupportIfNeed(QueryObjType type) if (validStatus_ != E_OK) { return; } - if (type != QueryObjType::IN && type != QueryObjType::EQUALTO && type != QueryObjType::AND && - type != QueryObjType::OR) { + static const std::set SUPPORT_TYPES = { + QueryObjType::IN, QueryObjType::EQUALTO, QueryObjType::AND, QueryObjType::OR, QueryObjType::NOT_EQUALTO + }; + if (SUPPORT_TYPES.find(type) == SUPPORT_TYPES.end()) { validStatus_ = -E_NOT_SUPPORT; } if (validStatus_ != E_OK) { diff --git a/frameworks/libs/distributeddb/common/src/relational/table_info.cpp b/frameworks/libs/distributeddb/common/src/relational/table_info.cpp index 657dd6140d1939603c0933eee97c74943fdab19f..5d9b098f518483809b0a9c4042d669c0304e7cdc 100644 --- a/frameworks/libs/distributeddb/common/src/relational/table_info.cpp +++ b/frameworks/libs/distributeddb/common/src/relational/table_info.cpp @@ -931,6 +931,42 @@ const std::vector TableInfo::GetUniqueAndPkDefine() const return res; } +void TableInfo::SetCloudTable(const std::optional &table) +{ + cloudTable_ = table; +} + +std::vector TableInfo::GetCloudSyncDistributedPk() const +{ + std::vector res; + if (!cloudTable_.has_value()) { + return res; + } + for (const auto &field : cloudTable_.value().fields) { + if (field.dupCheckCol) { + res.push_back(field.colName); + } + } + return res; +} + +std::vector TableInfo::GetCloudSyncFields() const +{ + std::vector res; + if (!cloudTable_.has_value()) { + return res; + } + for (const auto &field : cloudTable_.value().fields) { + res.push_back(field.colName); + } + return res; +} + +std::optional TableInfo::GetCloudTable() const +{ + return cloudTable_; +} + void TableInfo::SetType(const std::string &type) { type_ = type; diff --git a/frameworks/libs/distributeddb/distributeddb.gni b/frameworks/libs/distributeddb/distributeddb.gni index 6cb0f4055898a0d7966aa27918c92b0608528f2d..a2b0a3af0a43c478081e3b38069a5aca4ae459dd 100755 --- a/frameworks/libs/distributeddb/distributeddb.gni +++ b/frameworks/libs/distributeddb/distributeddb.gni @@ -266,10 +266,6 @@ distributeddb_src = [ ] distributeddb_cloud_src = [ - "${distributeddb_path}/syncer/src/cloud/cloud_force_pull_strategy.cpp", - "${distributeddb_path}/syncer/src/cloud/cloud_force_push_strategy.cpp", - "${distributeddb_path}/syncer/src/cloud/cloud_merge_strategy.cpp", - "${distributeddb_path}/syncer/src/cloud/cloud_sync_strategy.cpp", "${distributeddb_path}/syncer/src/cloud/cloud_db_proxy.cpp", "${distributeddb_path}/syncer/src/cloud/cloud_locker.cpp", "${distributeddb_path}/syncer/src/cloud/cloud_syncer.cpp", @@ -280,7 +276,14 @@ distributeddb_cloud_src = [ "${distributeddb_path}/syncer/src/cloud/cloud_sync_utils.cpp", "${distributeddb_path}/syncer/src/cloud/process_notifier.cpp", "${distributeddb_path}/syncer/src/cloud/process_recorder.cpp", - "${distributeddb_path}/syncer/src/cloud/strategy_factory.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy_proxy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/strategy_factory.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_custom_pull_strategy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_custom_push_strategy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_force_pull_strategy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_force_push_strategy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_merge_strategy.cpp", + "${distributeddb_path}/syncer/src/cloud/strategy/cloud_sync_strategy.cpp", ] distributeddb_src_rd = [ @@ -347,4 +350,4 @@ distributeddb_client_src = [ "${distributeddb_path}/interfaces/src/relational/knowledge_source_utils.cpp", "${distributeddb_path}/interfaces/src/relational/relational_store_client_utils.cpp", "${distributeddb_path}/interfaces/src/relational/relational_store_sqlite_ext.cpp", -] \ No newline at end of file +] diff --git a/frameworks/libs/distributeddb/interfaces/include/cloud/cloud_store_types.h b/frameworks/libs/distributeddb/interfaces/include/cloud/cloud_store_types.h index 64a2d508333b891a687a9fe713205c535d0a53b2..233f766b90fa2aa67bef0cd5e485681699205842 100644 --- a/frameworks/libs/distributeddb/interfaces/include/cloud/cloud_store_types.h +++ b/frameworks/libs/distributeddb/interfaces/include/cloud/cloud_store_types.h @@ -71,10 +71,12 @@ struct Field { int32_t type; // get value from TYPE_INDEX; bool primary = false; bool nullable = true; + bool dupCheckCol = false; // use for calculate hash_key when it was true bool operator==(const Field &comparedField) const { return (colName == comparedField.colName) && (type == comparedField.type) && - (primary == comparedField.primary) && (nullable == comparedField.nullable); + (primary == comparedField.primary) && (nullable == comparedField.nullable) && + (dupCheckCol == comparedField.dupCheckCol); } }; @@ -123,6 +125,7 @@ enum class QueryNodeType : uint32_t { OR = 0x101, AND, EQUAL_TO = 0x201, + NOT_EQUAL_TO, BEGIN_GROUP = 0x301, END_GROUP }; @@ -163,5 +166,10 @@ struct AsyncDownloadAssetsConfig { uint32_t maxDownloadTask = 1; // valid range in [1, 12] max async download task in process uint32_t maxDownloadAssetsCount = 100; // valid range in [1, 2000] max async download assets count in one batch }; + +enum class TaskType : uint32_t { + BACKGROUND_TASK = 0, + BUTT +}; } // namespace DistributedDB #endif // CLOUD_STORE_TYPE_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/interfaces/include/cloud/icloud_conflict_handler.h b/frameworks/libs/distributeddb/interfaces/include/cloud/icloud_conflict_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..665bdda8618ed1455f4bd64ff4b88cff01158af9 --- /dev/null +++ b/frameworks/libs/distributeddb/interfaces/include/cloud/icloud_conflict_handler.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ICLOUD_CONFLICT_HANDLER_H +#define ICLOUD_CONFLICT_HANDLER_H + +#include "cloud_store_types.h" + +namespace DistributedDB { +enum class ConflictRet : uint32_t { + UPSERT = 0, + DELETE, + NOT_HANDLE, + INTEGRATE, // update data but no change flag + BUTT +}; + +class ICloudConflictHandler { +public: + ICloudConflictHandler() = default; + virtual ~ICloudConflictHandler() = default; + virtual ConflictRet HandleConflict(const std::string &table, const VBucket &oldData, const VBucket &newData, + VBucket &upsert) = 0; +}; +} +#endif // ICLOUD_CONFLICT_HANDLER_H diff --git a/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_delegate.h b/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_delegate.h index 3c3ebce76b7ff05273f201ff0ac7f935cd7f576d..ac6935ed13fd4fd476c21d42fb041e4c21955eb9 100644 --- a/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_delegate.h +++ b/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_delegate.h @@ -21,6 +21,7 @@ #include #include "distributeddb/result_set.h" #include "cloud/cloud_store_types.h" +#include "cloud/icloud_conflict_handler.h" #include "cloud/icloud_db.h" #include "cloud/icloud_data_translate.h" #include "cloud/iAssetLoader.h" @@ -50,14 +51,19 @@ public: std::optional tableMode; }; + struct CreateDistributedTableConfig { + bool isAsync = false; + }; + DB_API virtual DBStatus SetStoreConfig(const StoreConfig &config) { return OK; } - DB_API DBStatus CreateDistributedTable(const std::string &tableName, TableSyncType type = DEVICE_COOPERATION) + DB_API DBStatus CreateDistributedTable(const std::string &tableName, TableSyncType type = DEVICE_COOPERATION, + CreateDistributedTableConfig config = {false}) { - return CreateDistributedTableInner(tableName, type); + return CreateDistributedTableInner(tableName, type, config); } DB_API virtual DBStatus Sync(const std::vector &devices, SyncMode mode, @@ -203,6 +209,17 @@ public: return OK; } + DB_API virtual DBStatus SetCloudConflictHandler( + [[gnu::unused]] const std::shared_ptr &handler) + { + return OK; + } + + DB_API virtual DBStatus StopTask([[gnu::unused]] TaskType type) + { + return OK; + } + // tableMap: key is tableName, value is keepDevices DB_API virtual DBStatus RemoveExceptDeviceData(const std::map> &tableMap) { @@ -210,7 +227,8 @@ public: } protected: virtual DBStatus RemoveDeviceDataInner(const std::string &device, ClearMode mode) = 0; - virtual DBStatus CreateDistributedTableInner(const std::string &tableName, TableSyncType type) = 0; + virtual DBStatus CreateDistributedTableInner(const std::string &tableName, TableSyncType type, + const CreateDistributedTableConfig &config) = 0; virtual DBStatus RemoveDeviceTableDataInner(const ClearDeviceDataOption &option) { return DBStatus::OK; diff --git a/frameworks/libs/distributeddb/interfaces/include/store_types.h b/frameworks/libs/distributeddb/interfaces/include/store_types.h index c8e45f44066a8a5296f12a46dcaad903ddb0eab2..764f3969906d0954644ca58ac6b330284863ceef 100644 --- a/frameworks/libs/distributeddb/interfaces/include/store_types.h +++ b/frameworks/libs/distributeddb/interfaces/include/store_types.h @@ -100,6 +100,7 @@ enum DBStatus { TABLE_NOT_FOUND, SKIP_WHEN_CLOUD_SPACE_INSUFFICIENT, // Whitelist for contact, skip when cloud space insufficient EXPIRED_CURSOR, // Cursor is out of date in cloud + TASK_INTERRUPTED, // Task(cloud sync) interrupted BUTT_STATUS = 27394048 // end of status }; @@ -153,6 +154,8 @@ enum SyncMode { SYNC_MODE_CLOUD_MERGE = 4, SYNC_MODE_CLOUD_FORCE_PUSH, SYNC_MODE_CLOUD_FORCE_PULL, + SYNC_MODE_CLOUD_CUSTOM_PUSH, + SYNC_MODE_CLOUD_CUSTOM_PULL, }; enum ConflictResolvePolicy { diff --git a/frameworks/libs/distributeddb/interfaces/src/kv_store_errno.cpp b/frameworks/libs/distributeddb/interfaces/src/kv_store_errno.cpp index a6608ce1c0710fb467bf402cfbcc1b39ffcda1b8..ab4bf0a4393e5119662aa273b7124d14a54a9988 100644 --- a/frameworks/libs/distributeddb/interfaces/src/kv_store_errno.cpp +++ b/frameworks/libs/distributeddb/interfaces/src/kv_store_errno.cpp @@ -91,6 +91,7 @@ namespace { { -E_TABLE_NOT_FOUND, TABLE_NOT_FOUND }, { -E_SKIP_WHEN_CLOUD_SPACE_INSUFFICIENT, SKIP_WHEN_CLOUD_SPACE_INSUFFICIENT }, { -E_EXPIRED_CURSOR, EXPIRED_CURSOR }, + { -E_TASK_INTERRUPTED, TASK_INTERRUPTED }, }; } diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp index 3d069be107a26f74336eff3b0f2c3f250e1de7e9..432771ae9818e20f70e1278bfad25bf1cf22dbde 100644 --- a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.cpp @@ -96,7 +96,8 @@ DBStatus RelationalStoreDelegateImpl::RemoveDeviceTableDataInner(const ClearDevi return NOT_SUPPORT; } -DBStatus RelationalStoreDelegateImpl::CreateDistributedTableInner(const std::string &tableName, TableSyncType type) +DBStatus RelationalStoreDelegateImpl::CreateDistributedTableInner(const std::string &tableName, TableSyncType type, + const CreateDistributedTableConfig &config) { LOGI("[RelationalStore Delegate] Create distributed table for [%s length[%u]], type[%d]", DBCommon::StringMiddleMasking(tableName).c_str(), tableName.length(), static_cast(type)); @@ -111,6 +112,11 @@ DBStatus RelationalStoreDelegateImpl::CreateDistributedTableInner(const std::str return INVALID_ARGS; } + if (type != CLOUD_COOPERATION && config.isAsync) { + LOGE("[RelationalStore Delegate] Only supports create cloud distributed table asynchronously"); + return NOT_SUPPORT; + } + if (type == DEVICE_COOPERATION) { #ifndef USE_DISTRIBUTEDDB_DEVICE return OK; @@ -128,7 +134,7 @@ DBStatus RelationalStoreDelegateImpl::CreateDistributedTableInner(const std::str return DB_ERROR; } - int errCode = conn_->CreateDistributedTable(tableName, type); + int errCode = conn_->CreateDistributedTable(tableName, type, config.isAsync); auto duration = std::chrono::duration_cast(std::chrono::steady_clock::now() - start); if (duration > CloudDbConstant::DFX_TIME_THRESHOLD) { int64_t costTimeMs = duration.count(); @@ -615,6 +621,27 @@ DBStatus RelationalStoreDelegateImpl::ClearWatermark(const ClearMetaDataOption & return OK; } +DBStatus RelationalStoreDelegateImpl::SetCloudConflictHandler(const std::shared_ptr &handler) +{ + if (conn_ == nullptr) { + LOGE("[RelationalStore Delegate] Invalid connection for set cloud conflict handle"); + return DB_ERROR; + } + std::string userId; + std::string appId; + std::string storeId; + int errCode = conn_->GetStoreInfo(userId, appId, storeId); + if (errCode != E_OK) { + LOGE("[RelationalStore Delegate] Get storeInfo failed %d when set cloud conflict handle", errCode); + return TransferDBErrno(errCode); + } + errCode = conn_->SetCloudConflictHandler(handler); + LOGI("[RelationalStore Delegate] appId:%s storeId:%s SetCloudConflictHandle errCode[%d]", + DBCommon::StringMiddleMaskingWithLen(appId).c_str(), DBCommon::StringMiddleMaskingWithLen(storeId).c_str(), + errCode); + return TransferDBErrno(errCode); +} + std::pair RelationalStoreDelegateImpl::GetDownloadingAssetsCount() { if (conn_ == nullptr) { @@ -709,5 +736,17 @@ DBStatus RelationalStoreDelegateImpl::SetDistributedSchema(const DistributedSche return TransferDBErrno(errCode); } #endif + +#ifdef USE_DISTRIBUTEDDB_CLOUD +DBStatus RelationalStoreDelegateImpl::StopTask(TaskType type) +{ + LOGW("[RelationalStore Delegate] Stop task by user, type: %u", type); + if (conn_ == nullptr) { + LOGE("[RelationalStore Delegate] Invalid connection for stop task."); + return DB_ERROR; + } + return TransferDBErrno(conn_->StopTask(type)); +} +#endif } // namespace DistributedDB #endif \ No newline at end of file diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h index 925b9e1b001e36ae8992ab4d908e69abc3189a8c..9f67a7466581ce626c3b1c2db4f6926ec98a27b6 100644 --- a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_delegate_impl.h @@ -33,7 +33,8 @@ public: DBStatus RemoveDeviceTableDataInner(const ClearDeviceDataOption &option) override; - DBStatus CreateDistributedTableInner(const std::string &tableName, TableSyncType type) override; + DBStatus CreateDistributedTableInner(const std::string &tableName, TableSyncType type, + const CreateDistributedTableConfig &config) override; // For connection DBStatus Close(); @@ -75,8 +76,12 @@ public: DBStatus ClearMetaData(const ClearMetaDataOption &option) override; + DBStatus SetCloudConflictHandler(const std::shared_ptr &handler) override; + std::pair GetDownloadingAssetsCount() override; + DBStatus StopTask([[gnu::unused]] TaskType type) override; + DBStatus SetReference(const std::vector &tableReferenceProperty) override; DBStatus UpsertData(const std::string &tableName, const std::vector &records, diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_sync_able_storage.h b/frameworks/libs/distributeddb/interfaces/src/relational/relational_sync_able_storage.h index 390838d754f3b02e138ec52dfbd87dd7786a7204..62ceb71e43a52727a714f22c50e928901b3daff9 100644 --- a/frameworks/libs/distributeddb/interfaces/src/relational/relational_sync_able_storage.h +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_sync_able_storage.h @@ -278,6 +278,10 @@ public: int ConvertLogToLocal(const std::string &tableName, const std::vector &gids) override; + int WaitAsyncGenLogTaskFinished(const std::vector &tables) override; + + int ResetGenLogTaskStatus(); + int PutCloudGid(const std::string &tableName, std::vector &data) override; #ifdef USE_DISTRIBUTEDDB_CLOUD diff --git a/frameworks/libs/distributeddb/storage/include/cloud/cloud_storage_utils.h b/frameworks/libs/distributeddb/storage/include/cloud/cloud_storage_utils.h index 4e73d9783d50da746ec9dcb4b7f51736b5d7c8a6..bfd7386aebba7a71e85c48ea88e633efee2b8fd6 100644 --- a/frameworks/libs/distributeddb/storage/include/cloud/cloud_storage_utils.h +++ b/frameworks/libs/distributeddb/storage/include/cloud/cloud_storage_utils.h @@ -50,7 +50,8 @@ public: std::vector &value); static std::set GetCloudPrimaryKey(const TableSchema &tableSchema); - static std::vector GetCloudPrimaryKeyField(const TableSchema &tableSchema, bool sortByName = false); + static std::vector GetCloudPrimaryKeyField(const TableSchema &tableSchema); + static std::vector GetCloudPrimaryKeyField(const TableSchema &tableSchema, bool sortByName); static std::map GetCloudPrimaryKeyFieldMap(const TableSchema &tableSchema, bool sortByUpper = false); static bool IsContainsPrimaryKey(const TableSchema &tableSchema); diff --git a/frameworks/libs/distributeddb/storage/include/cloud/schema_mgr.h b/frameworks/libs/distributeddb/storage/include/cloud/schema_mgr.h index 71ffce60311140163e7e9833d0f12cc4c5144031..440d30e518510dcb4e7c9ffa2f075b7557302d17 100644 --- a/frameworks/libs/distributeddb/storage/include/cloud/schema_mgr.h +++ b/frameworks/libs/distributeddb/storage/include/cloud/schema_mgr.h @@ -43,6 +43,7 @@ private: bool ComparePrimaryField(std::map &localPrimaryKeys, const Field &cloudField); int CompareFieldSchema(std::map &primaryKeys, FieldInfoMap &localFields, std::vector &cloudFields); + int CheckCloudField(const Field &cloudField, FieldInfoMap &localFields, std::map &primaryKeys); std::shared_ptr cloudSchema_ = nullptr; std::map sharedTableMap_; }; diff --git a/frameworks/libs/distributeddb/storage/include/icloud_sync_storage_interface.h b/frameworks/libs/distributeddb/storage/include/icloud_sync_storage_interface.h index 82305a1ecd930ff813af80e74e59472a5ba01f42..f571b5c756976934429bef5e1a985900ee3e1d35 100644 --- a/frameworks/libs/distributeddb/storage/include/icloud_sync_storage_interface.h +++ b/frameworks/libs/distributeddb/storage/include/icloud_sync_storage_interface.h @@ -31,6 +31,7 @@ enum class OpType : uint8_t { INSERT = 1, UPDATE, // update data, gid and timestamp at same time DELETE, + INTEGRATE, // update data but no change flag ONLY_UPDATE_GID, // used in Cloud Force Push strategy, when SET_CLOUD_FORCE_PUSH_FLAG_ONE, upload process won't process this record SET_CLOUD_FORCE_PUSH_FLAG_ONE, @@ -327,6 +328,8 @@ public: return E_OK; } + virtual int WaitAsyncGenLogTaskFinished(const std::vector &tables) = 0; + virtual int PutCloudGid([[gnu::unused]] const std::string &tableName, [[gnu::unused]] std::vector &data) { return E_OK; diff --git a/frameworks/libs/distributeddb/storage/include/relational_store_connection.h b/frameworks/libs/distributeddb/storage/include/relational_store_connection.h index 913f1933a7bf9926c1ad4ce07b08caf1c7ce4ab0..8b2166f341d0fd5231f051a17fd1857f885698ef 100644 --- a/frameworks/libs/distributeddb/storage/include/relational_store_connection.h +++ b/frameworks/libs/distributeddb/storage/include/relational_store_connection.h @@ -51,7 +51,7 @@ public: virtual int Close() = 0; virtual std::string GetIdentifier() = 0; - virtual int CreateDistributedTable(const std::string &tableName, TableSyncType syncType) = 0; + virtual int CreateDistributedTable(const std::string &tableName, TableSyncType syncType, bool isAsync) = 0; virtual int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) = 0; virtual int RegisterObserverAction(const StoreObserver *observer, const RelationalObserverAction &action) = 0; @@ -91,6 +91,8 @@ public: virtual int Sync(const CloudSyncOption &option, const SyncProcessCallback &onProcess, uint64_t taskId) = 0; virtual SyncProcess GetCloudTaskStatus(uint64_t taskId) = 0; + + virtual int SetCloudConflictHandler(const std::shared_ptr &handler) = 0; #endif #ifdef USE_DISTRIBUTEDDB_DEVICE @@ -114,6 +116,8 @@ public: virtual int OperateDataStatus(uint32_t dataOperator) = 0; virtual int SetProperty(const Property &property) = 0; + + virtual int StopTask(TaskType type) = 0; protected: // Get the stashed 'RelationalDB_ pointer' without ref. template diff --git a/frameworks/libs/distributeddb/storage/include/storage_proxy.h b/frameworks/libs/distributeddb/storage/include/storage_proxy.h index 95edd504f3e86f90c348e849013ce31ab5559f42..c3fac43127377b8c8e42e1f093edcc231f7d0cf7 100644 --- a/frameworks/libs/distributeddb/storage/include/storage_proxy.h +++ b/frameworks/libs/distributeddb/storage/include/storage_proxy.h @@ -195,6 +195,10 @@ public: void FilterDownloadRecordNotFound(const std::string &tableName, DownloadData &downloadData); + void FilterDownloadRecordNoneSchemaField(const std::string &tableName, DownloadData &downloadData); + + int WaitAsyncGenLogTaskFinished(const std::vector &tables) const; + // gid contain in data with key #_gid int PutCloudGid(const std::string &tableName, std::vector &data); diff --git a/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp index 90c490feeb0277973d8c0e42f5f4e30f2cdbb964..bb8655df85507c3cb6d96de0210c3d9276331629 100644 --- a/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp +++ b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp @@ -103,7 +103,7 @@ int CloudStorageUtils::BindText(int index, const VBucket &vBucket, const Field & } if (errCode != E_OK) { - LOGE("Bind string to insert statement failed, %d", errCode); + LOGE("Bind string to statement failed, %d", errCode); } return errCode; } @@ -203,11 +203,10 @@ int CloudStorageUtils::BindAsset(int index, const VBucket &vBucket, const Field std::set CloudStorageUtils::GetCloudPrimaryKey(const TableSchema &tableSchema) { + std::vector pkVec = GetCloudPrimaryKeyField(tableSchema); std::set pkSet; - for (const auto &field : tableSchema.fields) { - if (field.primary) { - pkSet.insert(field.colName); - } + for (const auto &field : pkVec) { + pkSet.insert(field.colName); } return pkSet; } @@ -224,14 +223,27 @@ std::vector CloudStorageUtils::GetCloudAsset(const TableSchema &tableSche return assetFields; } -std::vector CloudStorageUtils::GetCloudPrimaryKeyField(const TableSchema &tableSchema, bool sortByName) +std::vector CloudStorageUtils::GetCloudPrimaryKeyField(const TableSchema &tableSchema) { std::vector pkVec; + std::vector dupCheckColVec; for (const auto &field : tableSchema.fields) { if (field.primary) { pkVec.push_back(field); } + if (field.dupCheckCol) { + dupCheckColVec.push_back(field); + } + } + if (dupCheckColVec.empty()) { + return pkVec; } + return dupCheckColVec; +} + +std::vector CloudStorageUtils::GetCloudPrimaryKeyField(const TableSchema &tableSchema, bool sortByName) +{ + std::vector pkVec = GetCloudPrimaryKeyField(tableSchema); if (sortByName) { std::sort(pkVec.begin(), pkVec.end(), [](const Field &a, const Field &b) { return a.colName < b.colName; @@ -243,14 +255,13 @@ std::vector CloudStorageUtils::GetCloudPrimaryKeyField(const TableSchema std::map CloudStorageUtils::GetCloudPrimaryKeyFieldMap(const TableSchema &tableSchema, bool sortByUpper) { + std::vector pkVec = GetCloudPrimaryKeyField(tableSchema); std::map pkMap; - for (const auto &field : tableSchema.fields) { - if (field.primary) { - if (sortByUpper) { - pkMap[DBCommon::ToUpperCase(field.colName)] = field; - } else { - pkMap[field.colName] = field; - } + for (const auto &field : pkVec) { + if (sortByUpper) { + pkMap[DBCommon::ToUpperCase(field.colName)] = field; + } else { + pkMap[field.colName] = field; } } return pkMap; @@ -645,7 +656,8 @@ static bool IsViolationOfConstraints(const std::string &name, const std::vector< int CloudStorageUtils::ConstraintsCheckForCloud(const TableInfo &table, const std::string &trimmedSql) { - if (DBCommon::HasConstraint(trimmedSql, "UNIQUE", " ,", " ,)(")) { + if (table.GetCloudSyncDistributedPk().empty() && + DBCommon::HasConstraint(trimmedSql, "UNIQUE", " ,", " ,)(")) { LOGE("[ConstraintsCheckForCloud] Not support create distributed table with 'UNIQUE' constraint."); return -E_NOT_SUPPORT; } diff --git a/frameworks/libs/distributeddb/storage/src/cloud/schema_mgr.cpp b/frameworks/libs/distributeddb/storage/src/cloud/schema_mgr.cpp index 5035d5a669b8ede7d1f18dba9b79497e6cdb4f81..4c6cc6b82f1bf9d2a250bfdb9001708a74fbb24a 100644 --- a/frameworks/libs/distributeddb/storage/src/cloud/schema_mgr.cpp +++ b/frameworks/libs/distributeddb/storage/src/cloud/schema_mgr.cpp @@ -22,6 +22,8 @@ #include "cloud/cloud_store_types.h" #include "db_common.h" #include "db_errno.h" +#include "sqlite_relational_utils.h" + namespace DistributedDB { SchemaMgr::SchemaMgr() { @@ -60,37 +62,19 @@ int SchemaMgr::CompareFieldSchema(std::map &primaryKeys, FieldIn std::vector &cloudFields) { std::unordered_set cloudColNames; + bool isContainDupCheckField = false; for (const Field &cloudField : cloudFields) { - if (localFields.find(cloudField.colName) == localFields.end()) { - LOGE("Column name mismatch between local and cloud schema, cloud column %s not found in local schema", - DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); - return -E_SCHEMA_MISMATCH; - } - if (IsAssetPrimaryField(cloudField)) { - LOGE("Asset type can not be primary field"); - return -E_SCHEMA_MISMATCH; + int errCode = CheckCloudField(cloudField, localFields, primaryKeys); + if (errCode != E_OK) { + return errCode; } - FieldInfo &localField = localFields[cloudField.colName]; - if (!CompareType(localField, cloudField)) { - StorageType localType = localField.GetStorageType(); - LOGE("Type mismatch between local and cloud schema, column = %s, local type = %d, cloud type = %d", - DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str(), - static_cast(localType), cloudField.type); - return -E_SCHEMA_MISMATCH; - } - if (!CompareNullable(localField, cloudField)) { - LOGE("The nullable property is mismatched between local and cloud schema, column = %s", - DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); - return -E_SCHEMA_MISMATCH; - } - if (!ComparePrimaryField(primaryKeys, cloudField)) { - LOGE("The primary key property is mismatched between local and cloud schema, column = %s", - DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); - return -E_SCHEMA_MISMATCH; + if (cloudField.dupCheckCol) { + isContainDupCheckField = true; } cloudColNames.emplace(cloudField.colName); } - if (!primaryKeys.empty() && !(primaryKeys.size() == 1 && primaryKeys[0] == DBConstant::ROWID)) { + if (!isContainDupCheckField && !primaryKeys.empty() && + !(primaryKeys.size() == 1 && primaryKeys[0] == DBConstant::ROWID)) { LOGE("Local schema contain extra primary key:%d", -E_SCHEMA_MISMATCH); return -E_SCHEMA_MISMATCH; } @@ -106,6 +90,39 @@ int SchemaMgr::CompareFieldSchema(std::map &primaryKeys, FieldIn return E_OK; } +int SchemaMgr::CheckCloudField(const Field &cloudField, FieldInfoMap &localFields, + std::map &primaryKeys) +{ + if (localFields.find(cloudField.colName) == localFields.end()) { + LOGE("Column name mismatch between local and cloud schema, cloud column %s not found in local schema", + DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); + return -E_SCHEMA_MISMATCH; + } + if (IsAssetPrimaryField(cloudField)) { + LOGE("Asset type can not be primary field"); + return -E_SCHEMA_MISMATCH; + } + FieldInfo &localField = localFields[cloudField.colName]; + if (!CompareType(localField, cloudField)) { + StorageType localType = localField.GetStorageType(); + LOGE("Type mismatch between local and cloud schema, column = %s, local type = %d, cloud type = %d", + DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str(), + static_cast(localType), cloudField.type); + return -E_SCHEMA_MISMATCH; + } + if (!CompareNullable(localField, cloudField)) { + LOGE("The nullable property is mismatched between local and cloud schema, column = %s", + DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); + return -E_SCHEMA_MISMATCH; + } + if (!ComparePrimaryField(primaryKeys, cloudField)) { + LOGE("The primary key property is mismatched between local and cloud schema, column = %s", + DBCommon::StringMiddleMaskingWithLen(cloudField.colName).c_str()); + return -E_SCHEMA_MISMATCH; + } + return E_OK; +} + bool SchemaMgr::IsAssetPrimaryField(const Field &cloudField) { return cloudField.primary && (cloudField.type == TYPE_INDEX || cloudField.type == TYPE_INDEX); @@ -142,6 +159,10 @@ bool SchemaMgr::CompareNullable(const FieldInfo &localField, const Field &cloudF bool SchemaMgr::ComparePrimaryField(std::map &localPrimaryKeys, const Field &cloudField) { + if (cloudField.dupCheckCol) { + // dup check field ignored its primary + return true; + } // whether the corresponding field in local schema is primary key bool isLocalFieldPrimary = false; for (const auto &kvPair : localPrimaryKeys) { @@ -167,18 +188,7 @@ void SchemaMgr::SetCloudDbSchema(const DataBaseSchema &schema, RelationalSchemaO missingTables++; continue; } - FieldInfoMap localFields = tableInfo.GetFields(); - - // remove the fields that are not found in local schema from cloud schema - for (auto it = table.fields.begin(); it != table.fields.end();) { - if (localFields.find((*it).colName) == localFields.end()) { - LOGW("Column mismatch, colName: %s, length: %zu", DBCommon::StringMiddleMasking((*it).colName).c_str(), - (*it).colName.length()); - it = table.fields.erase(it); - } else { - ++it; - } - } + SQLiteRelationalUtils::FilterTableSchema(tableInfo, table); } if (missingTables > 0u) { LOGD("Local schema does not contain following %" PRIu32 " tables: %s", missingTables, msg.c_str()); diff --git a/frameworks/libs/distributeddb/storage/src/data_transformer.h b/frameworks/libs/distributeddb/storage/src/data_transformer.h index 4612006608a15c080ab807073a834bff16d98f38..1a7fa30f9795505e5a8a646c67d7739a02eb6a11 100644 --- a/frameworks/libs/distributeddb/storage/src/data_transformer.h +++ b/frameworks/libs/distributeddb/storage/src/data_transformer.h @@ -89,6 +89,8 @@ struct OptTableDataWithLog { struct DataInfoWithLog { LogInfo logInfo; VBucket primaryKeys; + VBucket localData; + bool isQueryLocalData = false; }; struct UpdateRecordFlagStruct { diff --git a/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage.cpp b/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage.cpp index 87926092fe26bd972e444fbb80a9595a47c8966f..bb221514525269bb6bb4dc78bddff3ffed594c2d 100644 --- a/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage.cpp +++ b/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage.cpp @@ -1293,7 +1293,11 @@ int RelationalSyncAbleStorage::GetInfoByPrimaryKeyOrGidInner(SQLiteSingleVerRela } RelationalSchemaObject localSchema = GetSchemaInfo(); handle->SetLocalSchema(localSchema); - return handle->GetInfoByPrimaryKeyOrGid(tableSchema, vBucket, dataInfoWithLog, assetInfo); + errCode = handle->GetInfoByPrimaryKeyOrGid(tableSchema, vBucket, dataInfoWithLog, assetInfo); + if (errCode != E_OK) { + return errCode; + } + return handle->GetLocalDataByRowid(localSchema.GetTable(tableSchema.name), tableSchema, dataInfoWithLog); } int RelationalSyncAbleStorage::PutCloudSyncData(const std::string &tableName, DownloadData &downloadData) diff --git a/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage_extend.cpp b/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage_extend.cpp index fcdda87d9458837928071a9105575f0c15b575c7..8b72400e7438950822f2fa4e9744e27958ef6e2f 100644 --- a/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage_extend.cpp +++ b/frameworks/libs/distributeddb/storage/src/relational/relational_sync_able_storage_extend.cpp @@ -695,6 +695,35 @@ int RelationalSyncAbleStorage::ConvertLogToLocal(const std::string &tableName, c return errCode; } +int RelationalSyncAbleStorage::WaitAsyncGenLogTaskFinished(const std::vector &tables) +{ + if (storageEngine_ == nullptr) { + LOGE("[WaitAsyncGenLogTaskFinished] Storage is null"); + return -E_INVALID_DB; + } + std::string localIdentity; + int errCode = syncAbleEngine_->GetLocalIdentity(localIdentity); + if (errCode != E_OK) { + LOGE("Get local identity failed: %d", errCode); + return errCode; + } + if (localIdentity.empty()) { + LOGE("Get empty local identity"); + return -E_INVALID_ARGS; + } + return storageEngine_->WaitAsyncGenLogTaskFinished(tables, localIdentity); +} + +int RelationalSyncAbleStorage::ResetGenLogTaskStatus() +{ + if (storageEngine_ == nullptr) { + LOGE("[SetGenLogTaskStatus] Storage is null"); + return -E_INVALID_DB; + } + storageEngine_->ResetGenLogTaskStatus(); + return E_OK; +} + int RelationalSyncAbleStorage::PutCloudGid(const std::string &tableName, std::vector &data) { #ifdef USE_DISTRIBUTEDDB_CLOUD diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/query_object.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/query_object.cpp index deeb6e1f4d877d3a4bd245e0814795c1244c43c9..d593c222b2e3b950ff04a7f187d072d84e9b7234 100755 --- a/frameworks/libs/distributeddb/storage/src/sqlite/query_object.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/query_object.cpp @@ -538,7 +538,8 @@ int QueryObject::CheckPrimaryKey(const std::map &primaryKeyMap) } std::set queryPkSet; for (const auto &queryObjNode : queryObjNodes_) { - if (queryObjNode.operFlag != QueryObjType::IN && queryObjNode.operFlag != QueryObjType::EQUALTO) { + if (queryObjNode.operFlag != QueryObjType::IN && queryObjNode.operFlag != QueryObjType::EQUALTO && + queryObjNode.operFlag != QueryObjType::NOT_EQUALTO) { continue; } std::string field = queryObjNode.fieldName; @@ -609,5 +610,15 @@ bool QueryObject::IsUseFromTables() const { return isUseFromTables_; } + +void QueryObject::SetRelaxForDelete(bool isRelaxForDelete) +{ + isRelaxForDelete_ = isRelaxForDelete; +} + +bool QueryObject::IsRelaxForDelete() const +{ + return isRelaxForDelete_; +} } diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/query_object.h b/frameworks/libs/distributeddb/storage/src/sqlite/query_object.h index 2c8b2c377e4ff85d27b0dfe59603b0955237f1e8..8e0a656a0e5a3b2f48514c78cf4fe500819ac7a4 100755 --- a/frameworks/libs/distributeddb/storage/src/sqlite/query_object.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/query_object.h @@ -88,6 +88,10 @@ public: std::string GetRemoteDev() const; bool IsUseFromTables() const; + + bool IsRelaxForDelete() const; + + void SetRelaxForDelete(bool isRelaxForDelete); protected: explicit QueryObject(const QueryExpression &queryExpression); static std::vector GetQueryExpressions(const Query &query); @@ -108,6 +112,7 @@ protected: AssetsGroupMap assetsGroupMap_; int assetsOnlyErrFlag_ = E_OK; bool isUseFromTables_ = false; + bool isRelaxForDelete_ = false; private: int Parse(); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/query_sync_object.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/query_sync_object.cpp index 62a9aaacb427da2cd209eb05731f26d82ddb31cf..649d1857d8de4e6d4e48803373c2c03f861e109c 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/query_sync_object.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/query_sync_object.cpp @@ -523,6 +523,9 @@ int QuerySyncObject::TransformNodeType(const QueryObjNode &objNode, QueryNode &n node.fieldName = CloudDbConstant::CLOUD_KV_FIELD_KEY; node.type = QueryNodeType::IN; break; + case QueryObjType::NOT_EQUALTO: + node.type = QueryNodeType::NOT_EQUAL_TO; + break; default: LOGE("[Query] not support type %d", static_cast(objNode.operFlag)); errCode = -E_NOT_SUPPORT; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.cpp index 7d5c447e3a19cbb6a3f519591a0da3668a8c5791..1151ee496fff0fc69819bb3f73e835e509385d47 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.cpp @@ -24,15 +24,28 @@ std::string CloudSyncLogTableManager::CalcPrimaryKeyHash(const std::string &refe { (void)identity; std::string sql; - FieldInfoMap fieldInfos = table.GetFields(); - if (table.GetPrimaryKey().size() == 1) { - std::string pkName = table.GetPrimaryKey().at(0); + const FieldInfoMap &fieldInfos = table.GetFields(); + std::vector sourceFields = table.GetCloudSyncDistributedPk(); + if (sourceFields.empty()) { + std::for_each(table.GetPrimaryKey().begin(), table.GetPrimaryKey().end(), [&sourceFields](const auto &item) { + sourceFields.push_back(item.second); + }); + } + return CalcPrimaryKeyHashInner(references, sourceFields, fieldInfos); +} + +std::string CloudSyncLogTableManager::CalcPrimaryKeyHashInner(const std::string &references, + const std::vector &sourceFields, const DistributedDB::FieldInfoMap &fieldInfos) const +{ + std::string sql; + if (sourceFields.size() == 1) { + std::string pkName = sourceFields.at(0); if (pkName == "rowid") { std::string collateStr = std::to_string(static_cast(CollateType::COLLATE_NONE)); // we use _rowid_ to reference sqlite inner rowid to avoid rowid is a user defined column, // user can't create distributed table with column named "_rowid_" sql = "calc_hash(" + references + "'" + std::string(DBConstant::SQLITE_INNER_ROWID) + "', " + - collateStr + ")"; + collateStr + ")"; } else { if (fieldInfos.find(pkName) == fieldInfos.end()) { return sql; @@ -42,8 +55,8 @@ std::string CloudSyncLogTableManager::CalcPrimaryKeyHash(const std::string &refe } } else { std::set primaryKeySet; // we need sort primary key by upper name - for (const auto &it : table.GetPrimaryKey()) { - primaryKeySet.emplace(DBCommon::ToUpperCase(it.second)); + for (const auto &it : sourceFields) { + primaryKeySet.emplace(DBCommon::ToUpperCase(it)); } sql = "calc_hash("; for (const auto &it : primaryKeySet) { @@ -104,24 +117,11 @@ std::string CloudSyncLogTableManager::GetInsertTrigger(const TableInfo &table, c std::string insertTrigger = "CREATE TRIGGER IF NOT EXISTS "; insertTrigger += "naturalbase_rdb_" + tableName + "_ON_INSERT AFTER INSERT \n"; insertTrigger += "ON '" + tableName + "'\n"; - insertTrigger += "WHEN (SELECT count(*) FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata "; - insertTrigger += "WHERE key = 'log_trigger_switch' AND value = 'true')\n"; + insertTrigger += GetInsertCondition(table); insertTrigger += "BEGIN\n"; insertTrigger += CloudStorageUtils::GetCursorIncSql(tableName) + "\n"; - insertTrigger += "\t INSERT OR REPLACE INTO " + logTblName; - insertTrigger += " (data_key, device, ori_device, timestamp, wtimestamp, flag, hash_key, cloud_gid, "; - insertTrigger += " extend_field, cursor, version, sharing_resource, status)"; - insertTrigger += " VALUES (new." + std::string(DBConstant::SQLITE_INNER_ROWID) + ", '', '',"; - insertTrigger += " get_raw_sys_time(), get_raw_sys_time(), 0x02|0x20, "; - insertTrigger += CalcPrimaryKeyHash("NEW.", table, identity) + ", CASE WHEN (SELECT count(*)<>0 FROM "; - insertTrigger += logTblName + " WHERE hash_key = " + CalcPrimaryKeyHash("NEW.", table, identity); - insertTrigger += ") THEN (SELECT cloud_gid FROM " + logTblName + " WHERE hash_key = "; - insertTrigger += CalcPrimaryKeyHash("NEW.", table, identity) + ") ELSE '' END, "; - insertTrigger += table.GetTrackerTable().GetAssignValSql(); - insertTrigger += ", " + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", "; - insertTrigger += "(SELECT CASE WHEN version IS NULL THEN '' ELSE version END FROM " + logTblName; - insertTrigger += " WHERE hash_key = " + CalcPrimaryKeyHash("NEW.", table, identity); - insertTrigger += "), '', 0);\n"; + insertTrigger += GetInsertLogSQL(table, identity, true); + insertTrigger += ";\n"; insertTrigger += CloudStorageUtils::GetTableRefUpdateSql(table, OpType::INSERT); insertTrigger += "SELECT client_observer('" + tableName + "', NEW." + std::string(DBConstant::SQLITE_INNER_ROWID); insertTrigger += ", 0, "; @@ -135,23 +135,14 @@ std::string CloudSyncLogTableManager::GetInsertTrigger(const TableInfo &table, c std::string CloudSyncLogTableManager::GetUpdateTrigger(const TableInfo &table, const std::string &identity) { (void)identity; - std::string logTblName = GetLogTableName(table); const std::string &tableName = table.GetTableName(); std::string updateTrigger = "CREATE TRIGGER IF NOT EXISTS "; updateTrigger += "naturalbase_rdb_" + tableName + "_ON_UPDATE AFTER UPDATE \n"; updateTrigger += "ON '" + tableName + "'\n"; - updateTrigger += "WHEN (SELECT count(*) FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata "; - updateTrigger += "WHERE key = 'log_trigger_switch' AND value = 'true')\n"; + updateTrigger += GetUpdateCondition(table); updateTrigger += "BEGIN\n"; // if user change the primary key, we can still use gid to identify which one is updated updateTrigger += CloudStorageUtils::GetCursorIncSql(tableName) + "\n"; - updateTrigger += "\t UPDATE " + logTblName; - updateTrigger += " SET timestamp=get_raw_sys_time(), device='', flag=((flag&0x1000)|0x02|0x20)&~0x4000"; - if (!table.GetTrackerTable().IsEmpty()) { - updateTrigger += table.GetTrackerTable().GetExtendAssignValSql(); - } - updateTrigger += ", cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", "; - updateTrigger += CloudStorageUtils::GetUpdateLockChangedSql(); - updateTrigger += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n"; + updateTrigger += GetUpdateLog(table, identity); updateTrigger += CloudStorageUtils::GetTableRefUpdateSql(table, OpType::UPDATE); updateTrigger += "SELECT client_observer('" + tableName + "', OLD."; updateTrigger += std::string(DBConstant::SQLITE_INNER_ROWID); @@ -214,4 +205,106 @@ std::vector CloudSyncLogTableManager::GetDropTriggers(const TableIn } return dropTriggers; } + +std::string CloudSyncLogTableManager::GetUpdateCondition(const TableInfo &table) +{ + std::string condition = "WHEN (SELECT count(*) FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata "; + condition += "WHERE key = 'log_trigger_switch' AND value = 'true'"; + auto fields = table.GetCloudSyncFields(); + auto pk = table.GetCloudSyncDistributedPk(); + for (const auto &field : pk) { + condition += " AND NEW.'" + field + "' IS NOT NULL"; + } + if (!pk.empty() && !fields.empty()) { + condition += " AND ("; + for (size_t i = 0; i < fields.size(); ++i) { + if (i != 0) { + condition += " OR "; + } + condition += "OLD.'" + fields[i] + "' IS DISTINCT FROM NEW.'" + fields[i] + "'"; + } + condition += ")"; + } + condition += ")\n"; + return condition; +} + +std::string CloudSyncLogTableManager::GetInsertCondition(const TableInfo &table) +{ + std::string condition = "WHEN (SELECT count(*) FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata "; + condition += "WHERE key = 'log_trigger_switch' AND value = 'true'"; + auto pk = table.GetCloudSyncDistributedPk(); + for (const auto &field : pk) { + condition += " AND NEW." + field + " IS NOT NULL"; + } + condition += ")\n"; + return condition; +} + +std::string CloudSyncLogTableManager::GetInsertLogSQL(const TableInfo &table, const std::string &identity, + bool isReplace) +{ + std::string sql; + auto &tableName = table.GetTableName(); + auto logTblName = DBCommon::GetLogTableName(tableName); + sql += "\t INSERT "; + if (isReplace) { + sql += "OR REPLACE "; + } + sql += "INTO " + logTblName; + sql += " (data_key, device, ori_device, timestamp, wtimestamp, flag, hash_key, cloud_gid, "; + sql += " extend_field, cursor, version, sharing_resource, status)"; + sql += " VALUES (new." + std::string(DBConstant::SQLITE_INNER_ROWID) + ", '', '',"; + sql += " get_raw_sys_time(), get_raw_sys_time(), 0x02|0x20, "; + sql += CalcPrimaryKeyHash("NEW.", table, identity) + ", CASE WHEN (SELECT count(*)<>0 FROM "; + sql += logTblName + " WHERE hash_key = " + CalcPrimaryKeyHash("NEW.", table, identity); + sql += ") THEN (SELECT cloud_gid FROM " + logTblName + " WHERE hash_key = "; + sql += CalcPrimaryKeyHash("NEW.", table, identity) + ") ELSE '' END, "; + sql += table.GetTrackerTable().GetAssignValSql(); + sql += ", " + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", "; + sql += "(SELECT CASE WHEN version IS NULL THEN '' ELSE version END FROM " + logTblName; + sql += " WHERE hash_key = " + CalcPrimaryKeyHash("NEW.", table, identity); + sql += "), '', 0)"; + return sql; +} + +std::string CloudSyncLogTableManager::GetUpdateConflictKey(const TableInfo &table) +{ + auto primaryKey = table.GetPrimaryKey(); + if (primaryKey[0] == DBConstant::ROWID) { + return "hash_key, cloud_gid"; + } + return "hash_key"; +} + +std::string CloudSyncLogTableManager::GetUpdateLog(const TableInfo &table, const std::string &identity) +{ + auto pk = table.GetCloudSyncDistributedPk(); + std::string logTblName = GetLogTableName(table); + const std::string &tableName = table.GetTableName(); + std::string updateTrigger; + if (pk.empty()) { + updateTrigger += "\t UPDATE " + logTblName; + updateTrigger += " SET timestamp=get_raw_sys_time(), device='', flag=" + + std::string(CloudDbConstant::LOCAL_UPDATE_FLAG); + if (!table.GetTrackerTable().IsEmpty()) { + updateTrigger += table.GetTrackerTable().GetExtendAssignValSql(); + } + updateTrigger += ", cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", "; + updateTrigger += CloudStorageUtils::GetUpdateLockChangedSql(); + updateTrigger += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n"; + return updateTrigger; + } + updateTrigger += GetInsertLogSQL(table, identity, false); + updateTrigger += " ON CONFLICT(" + GetUpdateConflictKey(table) + ") DO UPDATE "; + updateTrigger += " SET timestamp=get_raw_sys_time(), device='', flag=" + + std::string(CloudDbConstant::LOCAL_UPDATE_FLAG); + if (!table.GetTrackerTable().IsEmpty()) { + updateTrigger += table.GetTrackerTable().GetExtendAssignValSql(); + } + updateTrigger += ", cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", "; + updateTrigger += CloudStorageUtils::GetUpdateLockChangedSql(); + updateTrigger += ";\n"; + return updateTrigger; +} } // DistributedDB \ No newline at end of file diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.h index 44e0b4f5f6807bf2cacf804289aa8f253f91d2f7..81581bb15d3b021005568b7c0e5a472974329c72 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/cloud_sync_log_table_manager.h @@ -38,6 +38,13 @@ private: std::string GetUpdateTrigger(const TableInfo &table, const std::string &identity) override; std::string GetDeleteTrigger(const TableInfo &table, const std::string &identity) override; std::vector GetDropTriggers(const TableInfo &table) override; + std::string CalcPrimaryKeyHashInner(const std::string &references, const std::vector &sourceFields, + const FieldInfoMap &fieldInfos) const; + std::string GetInsertLogSQL(const TableInfo &table, const std::string &identity, bool isReplace); + std::string GetUpdateLog(const TableInfo &table, const std::string &identity); + static std::string GetUpdateCondition(const TableInfo &table); + static std::string GetInsertCondition(const TableInfo &table); + static std::string GetUpdateConflictKey(const TableInfo &table); }; } // DistributedDB diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp index e4202244a5088303c7bcd431745ed1745ddd1c93..b74c0b3ebbcc998ffd1fc2e663496565ec0565ab 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.cpp @@ -16,7 +16,6 @@ #include "sqlite_relational_store.h" #include -#include #include "cloud/cloud_storage_utils.h" #include "cloud_sync_utils.h" #include "db_common.h" @@ -27,7 +26,6 @@ #include "db_types.h" #include "param_check_utils.h" #include "res_finalizer.h" -#include "sqlite_log_table_manager.h" #include "sqlite_relational_utils.h" #include "sqlite_relational_store_connection.h" #include "storage_engine_manager.h" @@ -426,6 +424,7 @@ void SQLiteRelationalStore::ReleaseDBConnection(uint64_t connectionId, Relationa { if (connectionCount_.load() == 1) { sqliteStorageEngine_->SetConnectionFlag(false); + sqliteStorageEngine_->StopGenLogTask(true); } connectMutex_.lock(); @@ -444,11 +443,15 @@ void SQLiteRelationalStore::WakeUpSyncer() syncAbleEngine_->WakeUpSyncer(); } -int SQLiteRelationalStore::CreateDistributedTable(const std::string &tableName, TableSyncType syncType, +int SQLiteRelationalStore::CreateDistributedTable(const std::string &tableName, TableSyncType syncType, bool isAsync, bool trackerSchemaChanged) { + sqliteStorageEngine_->ResetGenLogTaskStatus(); RelationalSchemaObject localSchema = sqliteStorageEngine_->GetSchema(); TableInfo tableInfo = localSchema.GetTable(tableName); + TableSchema tableSchema; + (void)storageEngine_->GetCloudTableSchema(tableName, tableSchema); + tableInfo.SetCloudTable(tableSchema); if (!tableInfo.Empty()) { bool isSharedTable = tableInfo.GetSharedTableMark(); if (isSharedTable && !trackerSchemaChanged) { @@ -465,8 +468,14 @@ int SQLiteRelationalStore::CreateDistributedTable(const std::string &tableName, } } bool schemaChanged = false; - int errCode = sqliteStorageEngine_->CreateDistributedTable(tableName, DBCommon::TransferStringToHex(localIdentity), - schemaChanged, syncType, trackerSchemaChanged); + auto [errCode, param] = GetCreateDisTableParam(tableName, DBCommon::TransferStringToHex(localIdentity), + syncType, trackerSchemaChanged); + if (errCode != E_OK) { + LOGE("Get create distributed table param failed %d", errCode); + return errCode; + } + param.isAsync = isAsync; + errCode = sqliteStorageEngine_->CreateDistributedTable(param, schemaChanged); if (errCode != E_OK) { LOGE("Create distributed table failed. %d", errCode); } else { @@ -541,10 +550,14 @@ int SQLiteRelationalStore::CheckAndCollectCloudTables(ClearMode mode, const Rela int SQLiteRelationalStore::CleanCloudData(ClearMode mode, const std::vector &tableList) { + int errCode = StopGenLogTask(tableList); + if (errCode != E_OK) { + LOGE("[RelationalStore] stop gen log task failed when clean cloud data: %d", errCode); + return errCode; + } RelationalSchemaObject localSchema = sqliteStorageEngine_->GetSchema(); - TableInfoMap tables = localSchema.GetTables(); std::vector cloudTableNameList; - int errCode = CheckAndCollectCloudTables(mode, localSchema, tableList, cloudTableNameList); + errCode = CheckAndCollectCloudTables(mode, localSchema, tableList, cloudTableNameList); if (errCode != E_OK) { LOGE("[RelationalStore]no distributed tables exist, or the table names are all invalid or do not exist, %d", errCode); @@ -1151,17 +1164,21 @@ int SQLiteRelationalStore::PrepareAndSetCloudDbSchema(const DataBaseSchema &sche LOGE("[RelationalStore][PrepareAndSetCloudDbSchema] storageEngine was not initialized"); return -E_INVALID_DB; } - int errCode = CheckCloudSchema(schema); + auto [errCode, filterSchema] = FilterCloudDbSchema(schema); + if (errCode != E_OK) { + return errCode; + } + errCode = CheckCloudSchema(filterSchema); if (errCode != E_OK) { return errCode; } // delete, update and create shared table and its distributed table - errCode = ExecuteCreateSharedTable(schema); + errCode = ExecuteCreateSharedTable(filterSchema); if (errCode != E_OK) { LOGE("[RelationalStore] prepare shared table failed:%d", errCode); return errCode; } - return storageEngine_->SetCloudDbSchema(schema); + return storageEngine_->SetCloudDbSchema(filterSchema); } int SQLiteRelationalStore::SetIAssetLoader(const std::shared_ptr &loader) @@ -1209,7 +1226,11 @@ int SQLiteRelationalStore::Sync(const CloudSyncOption &option, const SyncProcess LOGE("[RelationalStore][Sync] storageEngine was not initialized"); return -E_INVALID_DB; } - int errCode = CheckBeforeSync(option); + int errCode = storageEngine_->ResetGenLogTaskStatus(); + if (errCode != E_OK) { + return errCode; + } + errCode = CheckBeforeSync(option); if (errCode != E_OK) { return errCode; } @@ -1303,7 +1324,7 @@ int SQLiteRelationalStore::CheckQueryValid(const CloudSyncOption &option) } std::vector object = QuerySyncObject::GetQuerySyncObject(option.query); bool isFromTable = object.empty(); - if (!option.priorityTask && !isFromTable) { + if (option.mode != SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH && !option.priorityTask && !isFromTable) { LOGE("[RelationalStore] not support normal sync with query"); return -E_NOT_SUPPORT; } @@ -1331,11 +1352,11 @@ int SQLiteRelationalStore::CheckQueryValid(const CloudSyncOption &option) if (errCode != E_OK) { return errCode; } - return CheckObjectValid(option.priorityTask, object, isFromTable); + return CheckObjectValid(option.priorityTask, object, isFromTable, option.mode); } int SQLiteRelationalStore::CheckObjectValid(bool priorityTask, const std::vector &object, - bool isFromTable) + bool isFromTable, SyncMode mode) { RelationalSchemaObject localSchema = sqliteStorageEngine_->GetSchema(); for (const auto &item : object) { @@ -1356,7 +1377,7 @@ int SQLiteRelationalStore::CheckObjectValid(bool priorityTask, const std::vector } std::string tableName = item.GetRelationTableName(); TableInfo tableInfo = localSchema.GetTable(tableName); - if (!tableInfo.Empty()) { + if (!tableInfo.Empty() && mode != SYNC_MODE_CLOUD_CUSTOM_PUSH) { const std::map &primaryKeyMap = tableInfo.GetPrimaryKey(); errCode = item.CheckPrimaryKey(primaryKeyMap); if (errCode != E_OK) { @@ -1386,43 +1407,27 @@ int SQLiteRelationalStore::CheckTableName(const std::vector &tableN void SQLiteRelationalStore::FillSyncInfo(const CloudSyncOption &option, const SyncProcessCallback &onProcess, CloudSyncer::CloudTaskInfo &info) { - auto syncObject = QuerySyncObject::GetQuerySyncObject(option.query); - if (syncObject.empty()) { - QuerySyncObject querySyncObject(option.query); - info.table = querySyncObject.GetRelationTableNames(); - for (const auto &item : info.table) { - QuerySyncObject object(Query::Select()); - object.SetTableName(item); - info.queryList.push_back(object); - } - } else { - for (auto &item : syncObject) { - info.table.push_back(item.GetRelationTableName()); - info.queryList.push_back(std::move(item)); - } - } - info.devices = option.devices; - info.mode = option.mode; - info.callback = onProcess; - info.timeout = option.waitTime; - info.priorityTask = option.priorityTask; - info.compensatedTask = option.compensatedSyncOnly; - info.priorityLevel = option.priorityLevel; - info.users.emplace_back(""); - info.lockAction = option.lockAction; - info.merge = option.merge; + SQLiteRelationalUtils::FillSyncInfo(option, onProcess, info); info.storeId = sqliteStorageEngine_->GetRelationalProperties().GetStringProp(DBProperties::STORE_ID, ""); - info.prepareTraceId = option.prepareTraceId; - info.asyncDownloadAssets = option.asyncDownloadAssets; } #endif int SQLiteRelationalStore::SetTrackerTable(const TrackerSchema &trackerSchema) { + std::vector> asyncTasks; + int errCode = sqliteStorageEngine_->GetAsyncGenLogTasksWithTables({trackerSchema.tableName}, asyncTasks); + if (errCode != E_OK) { + LOGE("[SetTrackerTable] Get async gen log tasks failed: %d", errCode); + return errCode; + } + if (!asyncTasks.empty()) { + LOGE("[SetTrackerTable] Not support set tracker table when async create distributed table"); + return -E_NOT_SUPPORT; + } TableInfo tableInfo; bool isFirstCreate = false; bool isNoTableInSchema = false; - int errCode = CheckTrackerTable(trackerSchema, tableInfo, isNoTableInSchema, isFirstCreate); + errCode = CheckTrackerTable(trackerSchema, tableInfo, isNoTableInSchema, isFirstCreate); if (errCode != E_OK) { if (errCode != -E_IGNORE_DATA) { return errCode; @@ -1444,7 +1449,7 @@ int SQLiteRelationalStore::SetTrackerTable(const TrackerSchema &trackerSchema) return errCode; } sqliteStorageEngine_->CacheTrackerSchema(trackerSchema); - errCode = CreateDistributedTable(trackerSchema.tableName, tableInfo.GetTableSyncType(), true); + errCode = CreateDistributedTable(trackerSchema.tableName, tableInfo.GetTableSyncType(), false, true); if (errCode != E_OK) { LOGE("[RelationalStore] create distributed table of [%s [%zu]] failed: %d", DBCommon::StringMiddleMasking(trackerSchema.tableName).c_str(), trackerSchema.tableName.size(), errCode); @@ -1849,9 +1854,14 @@ int SQLiteRelationalStore::SetCloudSyncConfig(const CloudSyncConfig &config) return E_OK; } -SyncProcess SQLiteRelationalStore::GetCloudTaskStatus(uint64_t taskId) +int SQLiteRelationalStore::SetCloudConflictHandler(const std::shared_ptr &handler) { - return cloudSyncer_->GetCloudTaskStatus(taskId); + if (cloudSyncer_ == nullptr) { + LOGE("[RelationalStore] cloudSyncer was not initialized when set conflict handler"); + return -E_INVALID_DB; + } + cloudSyncer_->SetCloudConflictHandler(handler); + return E_OK; } #endif @@ -2131,5 +2141,32 @@ void SQLiteRelationalStore::TrackerRepairImpl(TrackerTable &trackerTable, const LOGI("[TrackerIntegrityRepair] repair finish"); }); } + +std::pair SQLiteRelationalStore::GetCreateDisTableParam(const std::string &tableName, + const std::string &identity, TableSyncType syncType, bool isTrackerSchemaChanged) const +{ + std::pair res; + auto &[errCode, param] = res; + param.isTrackerSchemaChanged = isTrackerSchemaChanged; + param.identity = identity; + param.syncType = syncType; + param.tableName = tableName; +#ifdef USE_DISTRIBUTEDDB_CLOUD + if (storageEngine_ == nullptr) { + LOGW("[RelationalStore] storageEngine is null when create distributed table"); + errCode = -E_INVALID_DB; + return res; + } + TableSchema schema; + errCode = storageEngine_->GetCloudTableSchema(tableName, schema); + if (errCode == E_OK) { + param.cloudTable = schema; + } else { + LOGW("[RelationalStore] get cloud table err[%d] when create distributed table", errCode); + errCode = E_OK; + } +#endif + return res; +} } // namespace DistributedDB #endif diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h index 4a1c681d7f7bdffb6ad1db43ba8d631cf14d200a..d22e851d488fd24b27fd8179380d74c6993bdd14 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store.h @@ -18,6 +18,7 @@ #include #include +#include #include #include "irelational_store.h" @@ -57,7 +58,8 @@ public: return storageEngine_; } - int CreateDistributedTable(const std::string &tableName, TableSyncType syncType, bool trackerSchemaChanged = false); + int CreateDistributedTable(const std::string &tableName, TableSyncType syncType, bool isAsync, + bool trackerSchemaChanged = false); int RegisterObserverAction(uint64_t connectionId, const StoreObserver *observer, const RelationalObserverAction &action); @@ -112,7 +114,11 @@ public: int SetCloudSyncConfig(const CloudSyncConfig &config); - SyncProcess GetCloudTaskStatus(uint64_t taskId); + SyncProcess GetCloudTaskStatus(uint64_t taskId) const; + + int SetCloudConflictHandler(const std::shared_ptr &handler); + + int StopGenLogTask(const std::vector &tableList); #endif #ifdef USE_DISTRIBUTEDDB_DEVICE @@ -138,6 +144,8 @@ public: int OperateDataStatus(uint32_t dataOperator); int SetProperty(const Property &property); + + void StopAllBackgroundTask(); protected: void ReleaseResources(); @@ -177,7 +185,8 @@ protected: int CheckQueryValid(const CloudSyncOption &option); - int CheckObjectValid(bool priorityTask, const std::vector &object, bool isFromTable); + int CheckObjectValid(bool priorityTask, const std::vector &object, bool isFromTable, + SyncMode mode); int CheckTableName(const std::vector &tableNames); @@ -218,6 +227,8 @@ protected: CloudSyncer::CloudTaskInfo &info); int CheckCloudSchema(const DataBaseSchema &schema); + + std::pair FilterCloudDbSchema(const DataBaseSchema &schema); #endif int OperateDataStatusInner(const std::vector &tables, uint64_t virtualTime) const; @@ -231,6 +242,9 @@ protected: RelationalSchemaObject GetSchemaObj() const; + std::pair GetCreateDisTableParam(const std::string &tableName, + const std::string &identity, TableSyncType syncType, bool isTrackerSchemaChanged) const; + int CheckTableSyncType(const std::string &tableName, TableSyncType tableSyncType) const; int GetTargetDevices(const std::string &localDeviceId, const std::vector &keepDevices, diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp index dc7608e5d2943bab09ea1d1561d8088bcd5615dd..84eb650735b26d740115ea1a97b7c0041d278be6 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp @@ -143,7 +143,8 @@ int SQLiteRelationalStoreConnection::RollBack() return errCode; } -int SQLiteRelationalStoreConnection::CreateDistributedTable(const std::string &tableName, TableSyncType syncType) +int SQLiteRelationalStoreConnection::CreateDistributedTable(const std::string &tableName, TableSyncType syncType, + bool isAsync) { auto *store = GetDB(); if (store == nullptr) { @@ -151,7 +152,7 @@ int SQLiteRelationalStoreConnection::CreateDistributedTable(const std::string &t return -E_INVALID_CONNECTION; } - int errCode = store->CreateDistributedTable(tableName, syncType); + int errCode = store->CreateDistributedTable(tableName, syncType, isAsync); if (errCode != E_OK) { LOGE("[RelationalConnection] create distributed table failed. %d", errCode); } @@ -490,6 +491,16 @@ SyncProcess SQLiteRelationalStoreConnection::GetCloudTaskStatus(uint64_t taskId) DecObjRef(this); return process; } + +int SQLiteRelationalStoreConnection::SetCloudConflictHandler(const std::shared_ptr &handler) +{ + auto *store = GetDB(); + if (store == nullptr) { + LOGE("[RelationalConnection] store is null when set cloud conflict handle"); + return -E_INVALID_DB; + } + return store->SetCloudConflictHandler(handler); +} #endif #ifdef USE_DISTRIBUTEDDB_DEVICE @@ -566,5 +577,20 @@ int SQLiteRelationalStoreConnection::SetProperty(const Property &property) } return store->SetProperty(property); } + +int SQLiteRelationalStoreConnection::StopTask(TaskType type) +{ + if (static_cast(type) >= static_cast(TaskType::BUTT)) { + LOGE("[RelationalConnection] stop invalid task type %" PRIu32, static_cast(type)); + return -E_INVALID_ARGS; + } + auto *store = GetDB(); + if (store != nullptr) { + store->StopAllBackgroundTask(); + } else { + LOGW("[RelationalConnection] store is null when stop task"); + } + return E_OK; +} } #endif \ No newline at end of file diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h index 50e853e219e50b936b2f7b0c0ba1396281b4ae91..57a167c69640c6fa1bc7b6f6179f9e320439d891 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_connection.h @@ -36,7 +36,7 @@ public: int Close() override; std::string GetIdentifier() override; - int CreateDistributedTable(const std::string &tableName, TableSyncType syncType) override; + int CreateDistributedTable(const std::string &tableName, TableSyncType syncType, bool isAsync) override; int RegisterLifeCycleCallback(const DatabaseLifeCycleNotifier ¬ifier) override; int RegisterObserverAction(const StoreObserver *observer, const RelationalObserverAction &action) override; @@ -75,6 +75,8 @@ public: int Sync(const CloudSyncOption &option, const SyncProcessCallback &onProcess, uint64_t taskId) override; SyncProcess GetCloudTaskStatus(uint64_t taskId) override; + + int SetCloudConflictHandler(const std::shared_ptr &handler) override; #endif #ifdef USE_DISTRIBUTEDDB_DEVICE @@ -99,6 +101,8 @@ public: int OperateDataStatus(uint32_t dataOperator) override; int SetProperty(const Property &property) override; + + int StopTask(TaskType type) override; protected: int Pragma(int cmd, void *parameter) override; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_extend.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_extend.cpp index dbb941f59658e586bf268a7772be386c9746e8fe..64d1290e4d48921f14c66efb8b2a009194484148 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_extend.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_store_extend.cpp @@ -13,10 +13,67 @@ * limitations under the License. */ #ifdef RELATIONAL_STORE -#include #include "sqlite_relational_store.h" namespace DistributedDB { +void SQLiteRelationalStore::StopAllBackgroundTask() +{ +#ifdef USE_DISTRIBUTEDDB_CLOUD + if (sqliteStorageEngine_ == nullptr) { + LOGW("[RelationalStore] Storage engine was not initialized when stop all background task"); + } else { + sqliteStorageEngine_->StopGenLogTask(); + } + if (cloudSyncer_ == nullptr) { + LOGW("[RelationalStore] cloudSyncer was not initialized when stop all background task"); + } else { + (void) cloudSyncer_->StopSyncTask(nullptr, -E_TASK_INTERRUPTED); + } +#endif +} + +#ifdef USE_DISTRIBUTEDDB_CLOUD +int SQLiteRelationalStore::StopGenLogTask(const std::vector &tableList) +{ + if (sqliteStorageEngine_ == nullptr) { + return -E_INVALID_DB; + } + return sqliteStorageEngine_->StopGenLogTaskWithTables(tableList); +} + +SyncProcess SQLiteRelationalStore::GetCloudTaskStatus(uint64_t taskId) const +{ + return cloudSyncer_->GetCloudTaskStatus(taskId); +} + +std::pair SQLiteRelationalStore::FilterCloudDbSchema(const DataBaseSchema &schema) +{ + std::pair res; + auto &[errCode, databaseSchema] = res; + if (sqliteStorageEngine_ == nullptr) { + errCode = -E_INVALID_DB; + return res; + } + for (const auto &item : schema.tables) { + auto [ret, tableInfo] = sqliteStorageEngine_->AnalyzeTable(item.name); + if (ret == -E_NOT_FOUND) { + databaseSchema.tables.emplace_back(item); + continue; + } + if (ret != E_OK) { + LOGW("[SQLiteRelationalStore] FilterCloudDbSchema analyze table[%s] errCode[%d]", + DBCommon::StringMiddleMaskingWithLen(item.name).c_str(), ret); + errCode = ret; + return res; + } + TableSchema tableSchema = item; + SQLiteRelationalUtils::FilterTableSchema(tableInfo, tableSchema); + databaseSchema.tables.emplace_back(tableSchema); + } + return res; +} +#endif + #ifdef USE_DISTRIBUTEDDB_DEVICE int SQLiteRelationalStore::RemoveExceptDeviceData(const std::map> &tableMap) { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp index 8e7b5606ece28431ad3b52f01a98addc059d1efa..510fac325f2f88a51e7b7f86112a5523268bd955 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp @@ -1027,6 +1027,186 @@ const std::string SQLiteRelationalUtils::GetTempUpdateLogCursorTriggerSql(const return sql; } +int SQLiteRelationalUtils::GetLocalDataByRowid(sqlite3 *db, const TableInfo &table, const TableSchema &tableSchema, + DataInfoWithLog &dataInfoWithLog) +{ + if (db == nullptr) { + LOGE("[RDBUtils] Get local data by rowid without db"); + return -E_INVALID_ARGS; + } + if (!table.IsValid()) { + LOGE("[RDBUtils] Get local data by rowid without valid table"); + return -E_INVALID_ARGS; + } + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, GetQueryLocalDataSQL(table, dataInfoWithLog.logInfo.dataKey), stmt); + if (errCode != E_OK) { + LOGE("[RDBUtils] Get statement failed %d when get local data", errCode); + return errCode; + } + ResFinalizer finalizer([stmt]() { + sqlite3_stmt *releaseStmt = stmt; + int errCode = E_OK; + SQLiteUtils::ResetStatement(releaseStmt, true, errCode); + }); + errCode = SQLiteUtils::StepNext(stmt); + if (errCode != E_OK) { + LOGE("[RDBUtils] Step statement failed %d when get local data", errCode); + return errCode; + } + auto fieldInfos = MergeFieldFromSchema(tableSchema.fields, table.GetFieldInfos()); + for (int i = 0; i < static_cast(fieldInfos.size()); ++i) { + Type cloudValue; + errCode = GetCloudValueByType(stmt, fieldInfos[i].type, i, cloudValue); + if (errCode != E_OK) { + return errCode; + } + dataInfoWithLog.localData.insert({fieldInfos[i].colName, cloudValue}); + } + dataInfoWithLog.localData.insert({CloudDbConstant::MODIFY_FIELD, + static_cast(dataInfoWithLog.logInfo.timestamp)}); + dataInfoWithLog.localData.insert({CloudDbConstant::CREATE_FIELD, + static_cast(dataInfoWithLog.logInfo.wTimestamp)}); + dataInfoWithLog.localData.insert({CloudDbConstant::VERSION_FIELD, dataInfoWithLog.logInfo.version}); + return E_OK; +} + +std::string SQLiteRelationalUtils::GetQueryLocalDataSQL(const TableInfo &table, int64_t dataKey) +{ + std::string sql = "SELECT "; + auto fieldInfos = table.GetFieldInfos(); + for (size_t i = 0; i < fieldInfos.size(); ++i) { + if (i != 0) { + sql.append(","); + } + sql.append(fieldInfos[i].GetFieldName()); + } + sql.append(" FROM ").append(table.GetTableName()).append(" WHERE _rowid_=") + .append(std::to_string(dataKey)); + return sql; +} + +int SQLiteRelationalUtils::PutVBucketByType(VBucket &vBucket, const Field &field, Type &cloudValue) +{ + if (field.type == TYPE_INDEX && cloudValue.index() == TYPE_INDEX) { + Asset asset; + int errCode = RuntimeContext::GetInstance()->BlobToAsset(std::get(cloudValue), asset); + if (errCode != E_OK) { + return errCode; + } + if (!CloudStorageUtils::CheckAssetStatus({asset})) { + return -E_CLOUD_ERROR; + } + vBucket.insert_or_assign(field.colName, asset); + } else if (field.type == TYPE_INDEX && cloudValue.index() == TYPE_INDEX) { + Assets assets; + int errCode = RuntimeContext::GetInstance()->BlobToAssets(std::get(cloudValue), assets); + if (errCode != E_OK) { + return errCode; + } + if (CloudStorageUtils::IsAssetsContainDuplicateAsset(assets) || !CloudStorageUtils::CheckAssetStatus(assets)) { + return -E_CLOUD_ERROR; + } + vBucket.insert_or_assign(field.colName, assets); + } else { + vBucket.insert_or_assign(field.colName, cloudValue); + } + return E_OK; +} + +std::vector SQLiteRelationalUtils::MergeFieldFromSchema(const std::vector &originFields, + const std::vector &targetFields) +{ + std::vector res; + std::map fields; + for (const auto &item : originFields) { + fields[item.colName] = item; + } + for (const auto &item : targetFields) { + auto iter = fields.find(item.GetFieldName()); + if (iter != fields.end()) { + res.push_back(iter->second); + continue; + } + res.push_back(ConvertToField(item)); + } + return res; +} + +Field SQLiteRelationalUtils::ConvertToField(const FieldInfo &fieldInfo) +{ + Field field; + field.colName = fieldInfo.GetFieldName(); + field.nullable = !fieldInfo.IsNotNull(); + field.type = ConvertToType(fieldInfo.GetStorageType()); + return field; +} + +int32_t SQLiteRelationalUtils::ConvertToType(StorageType storageType) +{ + switch (storageType) { + case StorageType::STORAGE_TYPE_TEXT: + return TYPE_INDEX; + case StorageType::STORAGE_TYPE_INTEGER: + return TYPE_INDEX; + case StorageType::STORAGE_TYPE_REAL: + return TYPE_INDEX; + case StorageType::STORAGE_TYPE_BLOB: + return TYPE_INDEX; + case StorageType::STORAGE_TYPE_NULL: + case StorageType::STORAGE_TYPE_NONE: + default: + return TYPE_INDEX; + } +} + +std::vector SQLiteRelationalUtils::GetUserUpdateField(const VBucket &vBucket, const TableSchema &tableSchema) +{ + std::vector fields; + std::set useFields; + for (const auto &field : vBucket) { + if (field.first.empty() || field.first[0] == '#') { + continue; + } + useFields.insert(field.first); + } + for (const auto &field : tableSchema.fields) { + if (useFields.find(field.colName) == useFields.end()) { + continue; + } + fields.push_back(field); + } + return fields; +} + +std::vector SQLiteRelationalUtils::GetSaveSyncField(const VBucket &vBucket, const TableSchema &tableSchema, + bool isContainDupCheck) +{ + std::vector fields; + std::map colFields; + std::for_each(tableSchema.fields.begin(), tableSchema.fields.end(), [isContainDupCheck, &colFields, &fields] + (const Field &field) { + colFields.insert({field.colName, field}); + if (isContainDupCheck || !field.dupCheckCol) { + fields.push_back(field); + } + }); + for (const auto &[colName, val] : vBucket) { + if (colName.empty() || colName[0] == '#') { + continue; + } + auto iter = colFields.find(colName); + if (iter != colFields.end()) { + continue; + } + Field field; + field.colName = colName; + field.type = static_cast(val.index()); + fields.push_back(field); + } + return fields; +} + std::pair SQLiteRelationalUtils::AnalyzeTable(sqlite3 *db, const std::string &tableName) { std::pair res; @@ -1035,7 +1215,54 @@ std::pair SQLiteRelationalUtils::AnalyzeTable(sqlite3 *db, const return res; } +void SQLiteRelationalUtils::FilterTableSchema(const TableInfo &tableInfo, TableSchema &table) +{ + FieldInfoMap localFields = tableInfo.GetFields(); + // remove the fields that are not found in local schema from cloud schema + for (auto it = table.fields.begin(); it != table.fields.end();) { + if (localFields.find((*it).colName) == localFields.end()) { + LOGW("Column mismatch, colName: %s, length: %zu", DBCommon::StringMiddleMasking((*it).colName).c_str(), + (*it).colName.length()); + it = table.fields.erase(it); + } else { + ++it; + } + } +} + #ifdef USE_DISTRIBUTEDDB_CLOUD +void SQLiteRelationalUtils::FillSyncInfo(const CloudSyncOption &option, const SyncProcessCallback &onProcess, + ICloudSyncer::CloudTaskInfo &info) +{ + auto syncObject = QuerySyncObject::GetQuerySyncObject(option.query); + if (syncObject.empty()) { + QuerySyncObject querySyncObject(option.query); + info.table = querySyncObject.GetRelationTableNames(); + for (const auto &item : info.table) { + QuerySyncObject object(Query::Select()); + object.SetTableName(item); + info.queryList.push_back(object); + } + } else { + for (auto &item : syncObject) { + info.table.push_back(item.GetRelationTableName()); + info.queryList.push_back(std::move(item)); + } + } + info.devices = option.devices; + info.mode = option.mode; + info.callback = onProcess; + info.timeout = option.waitTime; + info.priorityTask = option.priorityTask; + info.compensatedTask = option.compensatedSyncOnly; + info.priorityLevel = option.priorityLevel; + info.users.emplace_back(""); + info.lockAction = option.lockAction; + info.merge = option.merge; + info.prepareTraceId = option.prepareTraceId; + info.asyncDownloadAssets = option.asyncDownloadAssets; +} + int SQLiteRelationalUtils::PutCloudGid(sqlite3 *db, const std::string &tableName, std::vector &data) { // create tmp table if table not exists diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h index 73c84a76c5739bdff64555e5cf4f305e939ba7af..40562d177fa3d7a6c30757810ebb293dbcec8913 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h @@ -18,6 +18,7 @@ #include #include "cloud/cloud_store_types.h" +#include "cloud/icloud_syncer.h" #include "data_value.h" #include "sqlite_import.h" #include "sqlite_single_ver_relational_storage_executor.h" @@ -79,10 +80,12 @@ public: static int InitKnowledgeTableTypeToMeta(sqlite3 *db, bool isMemory, const std::string &tableName); static int SetLogTriggerStatus(sqlite3 *db, bool status); + static constexpr const uint32_t BATCH_GEN_LOG_SIZE = 1000; struct GenLogParam { sqlite3 *db = nullptr; bool isMemory = false; bool isTrackerTable = false; + uint32_t batchLimit = 0; }; static int GeneTimeStrForLog(const TableInfo &tableInfo, GenLogParam ¶m, std::string &timeStr); @@ -127,9 +130,28 @@ public: static const std::string GetTempUpdateLogCursorTriggerSql(const std::string &tableName); + static int GetLocalDataByRowid(sqlite3 *db, const TableInfo &table, const TableSchema &tableSchema, + DataInfoWithLog &dataInfoWithLog); + + static int PutVBucketByType(VBucket &vBucket, const Field &field, Type &cloudValue); + + static Field ConvertToField(const FieldInfo &fieldInfo); + + static int32_t ConvertToType(StorageType storageType); + + static std::vector GetUserUpdateField(const VBucket &vBucket, const TableSchema &tableSchema); + + static std::vector GetSaveSyncField(const VBucket &vBucket, const TableSchema &tableSchema, + bool isContainDupCheck); + static std::pair AnalyzeTable(sqlite3 *db, const std::string &tableName); + static void FilterTableSchema(const TableInfo &tableInfo, TableSchema &table); + #ifdef USE_DISTRIBUTEDDB_CLOUD + static void FillSyncInfo(const CloudSyncOption &option, const SyncProcessCallback &onProcess, + ICloudSyncer::CloudTaskInfo &info); + static int PutCloudGid(sqlite3 *db, const std::string &tableName, std::vector &data); struct CloudNotExistRecord { @@ -182,6 +204,12 @@ private: static int UpdateLocalDataModifyTime(sqlite3 *db, const std::string &table, const std::string &modifyTime); static int BindAndStepDevicesToStatement(sqlite3_stmt *stmt, const std::vector &keepDevices); + + static std::vector MergeFieldFromSchema(const std::vector &originFields, + const std::vector &targetFields); + + static std::string GetQueryLocalDataSQL(const TableInfo &table, int64_t dataKey); + #ifdef USE_DISTRIBUTEDDB_CLOUD static int CheckUserCreateSharedTableInner(const TableSchema &oriTable, const TableInfo &sharedTableInfo); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp index acffab8feb4255f708d063cb3eac0cc916e92d18..c91a2454302c63d2d066bf3d7e245ef7b1a8a1b1 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp @@ -169,10 +169,18 @@ int SQLiteRelationalUtils::GeneLogInfoForExistedData(const std::string &identity sql += GetExtendValue(tableInfo.GetTrackerTable()); sql += ", 0, '', '', 0 FROM '" + tableName + "' AS a "; if (param.isTrackerTable) { - sql += " WHERE 1 = 1;"; + sql += "WHERE 1 = 1"; } else { - sql += "WHERE NOT EXISTS (SELECT 1 FROM " + logTable + " WHERE data_key = a._rowid_);"; + sql += "WHERE NOT EXISTS (SELECT 1 FROM " + logTable + " WHERE data_key = a._rowid_)"; } + auto pk = tableInfo.GetCloudSyncDistributedPk(); + for (const auto &item : pk) { + sql += " AND " + item + " IS NOT NULL"; + } + if (!param.isTrackerTable && param.batchLimit > 0) { + sql += " LIMIT " + std::to_string(param.batchLimit); + } + sql += ";"; errCode = trackerTable.ReBuildTempTrigger(param.db, TriggerMode::TriggerModeEnum::INSERT, [db = param.db, &sql]() { int ret = SQLiteUtils::ExecuteRawSQL(db, sql); if (ret != E_OK) { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp index 70f879697a25f886835dc62d8879c61d394188cd..e7ac4390273a6bd7278dbb839389f024064ab455 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp @@ -17,10 +17,11 @@ #include "db_common.h" #include "db_errno.h" +#include "log_table_manager_factory.h" #include "res_finalizer.h" +#include "runtime_context.h" #include "sqlite_relational_database_upgrader.h" #include "sqlite_single_ver_relational_storage_executor.h" -#include "sqlite_relational_utils.h" namespace DistributedDB { @@ -158,6 +159,7 @@ namespace { const std::string DEVICE_TYPE = "device"; const std::string CLOUD_TYPE = "cloud"; const std::string SYNC_TABLE_TYPE = "sync_table_type_"; +constexpr const char *ASYNC_GEN_LOG_TASK_PREFIX = "async_generate_log_task_"; int SaveSchemaToMetaTable(SQLiteSingleVerRelationalStorageExecutor *handle, const RelationalSchemaObject &schema) { @@ -212,20 +214,24 @@ int SaveSyncTableTypeAndDropFlagToMeta(SQLiteSingleVerRelationalStorageExecutor } } -int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::string &tableName, - const std::string &identity, bool &schemaChanged, TableSyncType syncType, bool trackerSchemaChanged) +int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const CreateDistributedTableParam ¶m, + bool &schemaChanged) { + const auto &tableName = param.tableName; + const auto &syncType = param.syncType; std::lock_guard autoLock(createDistributedTableMutex_); RelationalSchemaObject schema = GetSchema(); + auto tableInfo = schema.GetTable(tableName); + tableInfo.SetCloudTable(param.cloudTable); bool isUpgraded = false; - if (DBCommon::CaseInsensitiveCompare(schema.GetTable(tableName).GetTableName(), tableName)) { + if (DBCommon::CaseInsensitiveCompare(tableInfo.GetTableName(), tableName)) { LOGI("distributed table bas been created."); if (schema.GetTable(tableName).GetTableSyncType() != syncType) { LOGE("table sync type mismatch."); return -E_TYPE_MISMATCH; } isUpgraded = true; - int errCode = UpgradeDistributedTable(tableName, schemaChanged, syncType); + int errCode = UpgradeDistributedTable(tableInfo, schemaChanged, syncType); if (errCode != E_OK) { LOGE("Upgrade distributed table failed. %d", errCode); return errCode; @@ -238,12 +244,26 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::strin schemaChanged = true; } - int errCode = CreateDistributedTable(tableName, isUpgraded, identity, schema, syncType); + int errCode = CreateDistributedTable(param, isUpgraded, schema); if (errCode != E_OK) { LOGE("CreateDistributedTable failed. %d", errCode); return errCode; } - if (isUpgraded && (schemaChanged || trackerSchemaChanged)) { + if (param.isAsync) { + errCode = TriggerGenLogTask(param.identity); + if (errCode != E_OK) { + LOGE("Start async generate cloud log info task failed. %d", errCode); + RefObject::DecObjRef(this); + return errCode; + } + } else { + errCode = GenCloudLogInfoIfNeeded(param.tableName, param.identity); + if (errCode != E_OK) { + LOGE("Generate cloud log info failed. %d", errCode); + return errCode; + } + } + if (isUpgraded && (schemaChanged || param.isTrackerSchemaChanged)) { // Used for upgrading the stock data of the trackerTable errCode = GenLogInfoForUpgrade(tableName, schemaChanged); } @@ -265,7 +285,8 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedSharedTable(SQLiteSing return -E_NOT_SUPPORT; } bool isUpgraded = schema.GetTable(sharedTableName).GetTableName() == sharedTableName; - int errCode = CreateDistributedTable(handle, isUpgraded, "", table, schema); + CreateDistributedTableParam param; + int errCode = CreateDistributedTable(handle, isUpgraded, param, table, schema); if (errCode != E_OK) { LOGE("create distributed table failed. %d", errCode); return errCode; @@ -275,9 +296,11 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedSharedTable(SQLiteSing return errCode; } -int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::string &tableName, bool isUpgraded, - const std::string &identity, RelationalSchemaObject &schema, TableSyncType tableSyncType) +int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const CreateDistributedTableParam ¶m, + bool isUpgraded, RelationalSchemaObject &schema) { + const auto &tableName = param.tableName; + const auto &tableSyncType = param.syncType; LOGD("Create distributed table."); int errCode = E_OK; auto *handle = static_cast(FindExecutor(true, OperatePerm::NORMAL_PERM, @@ -297,16 +320,21 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::strin table.SetTableSyncType(tableSyncType); table.SetTrackerTable(GetTrackerSchema().GetTrackerTable(tableName)); table.SetDistributedTable(schema.GetDistributedTable(tableName)); + table.SetCloudTable(param.cloudTable); if (isUpgraded) { table.SetSourceTableReference(schema.GetTable(tableName).GetTableReference()); } - if (!table.GetTrackerTable().IsEmpty() && tableSyncType == TableSyncType::DEVICE_COOPERATION && + if (param.isAsync && !table.GetTrackerTable().GetTableName().empty()) { + LOGE("[CreateDistributedTable] not support async create distributed table on tracker table"); + return -E_NOT_SUPPORT; + } + if (!table.GetTrackerTable().IsEmpty() && param.syncType == TableSyncType::DEVICE_COOPERATION && GetRelationalProperties().GetDistributedTableMode() == DistributedTableMode::SPLIT_BY_DEVICE) { LOGE("not support create distributed table with split mode on tracker table."); (void)handle->Rollback(); return -E_NOT_SUPPORT; } - errCode = CreateDistributedTable(handle, isUpgraded, identity, table, schema); + errCode = CreateDistributedTable(handle, isUpgraded, param, table, schema); if (errCode != E_OK) { LOGE("create distributed table failed. %d", errCode); (void)handle->Rollback(); @@ -320,7 +348,7 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(const std::strin } int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(SQLiteSingleVerRelationalStorageExecutor *&handle, - bool isUpgraded, const std::string &identity, TableInfo &table, RelationalSchemaObject &schema) + bool isUpgraded, const CreateDistributedTableParam ¶m, TableInfo &table, RelationalSchemaObject &schema) { auto mode = GetRelationalProperties().GetDistributedTableMode(); TableSyncType tableSyncType = table.GetTableSyncType(); @@ -330,7 +358,7 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(SQLiteSingleVerR LOGE("init cursor to meta failed. %d", errCode); return errCode; } - errCode = handle->CreateDistributedTable(mode, isUpgraded, identity, table); + errCode = handle->CreateDistributedTable(mode, isUpgraded, param.identity, table, param.isAsync); if (errCode != E_OK) { LOGE("create distributed table failed. %d", errCode); return errCode; @@ -349,11 +377,15 @@ int SQLiteSingleRelationalStorageEngine::CreateDistributedTable(SQLiteSingleVerR errCode = SaveSyncTableTypeAndDropFlagToMeta(handle, tableName, tableSyncType); if (errCode != E_OK) { LOGE("Save sync table type or drop flag to meta table failed. %d", errCode); + return errCode; } - return errCode; + if (param.isAsync) { + return AddAsyncGenLogTask(handle, tableName); + } + return E_OK; } -int SQLiteSingleRelationalStorageEngine::UpgradeDistributedTable(const std::string &tableName, bool &schemaChanged, +int SQLiteSingleRelationalStorageEngine::UpgradeDistributedTable(const TableInfo &tableInfo, bool &schemaChanged, TableSyncType syncType) { LOGD("Upgrade distributed table."); @@ -374,7 +406,7 @@ int SQLiteSingleRelationalStorageEngine::UpgradeDistributedTable(const std::stri RelationalSchemaObject schema = GetSchema(); auto mode = GetRelationalProperties().GetDistributedTableMode(); - errCode = handle->UpgradeDistributedTable(tableName, mode, schemaChanged, schema, syncType); + errCode = handle->UpgradeDistributedTable(tableInfo, mode, schemaChanged, schema, syncType); if (errCode != E_OK) { LOGE("Upgrade distributed table failed. %d", errCode); (void)handle->Rollback(); @@ -1005,6 +1037,156 @@ int SQLiteSingleRelationalStorageEngine::CleanTrackerDeviceTable(const std::vect return errCode; } +int SQLiteSingleRelationalStorageEngine::GetAsyncGenLogTasks(std::vector> &asyncGenLogTasks) +{ + int errCode = E_OK; + auto *handle = static_cast(FindExecutor(false, OperatePerm::NORMAL_PERM, + errCode)); + if (handle == nullptr) { + return errCode; + } + ResFinalizer finalizer([&handle, this] { this->ReleaseExecutor(handle); }); + return GetAsyncGenLogTasks(handle, asyncGenLogTasks); +} + +int SQLiteSingleRelationalStorageEngine::GetAsyncGenLogTasks(const SQLiteSingleVerRelationalStorageExecutor *handle, + std::vector> &asyncGenLogTasks) +{ + std::map asyncTaskMap; + Key keyPrefix; + DBCommon::StringToVector(ASYNC_GEN_LOG_TASK_PREFIX, keyPrefix); + int errCode = handle->GetKvDataByPrefixKey(keyPrefix, asyncTaskMap); + if (errCode != E_OK) { + LOGE("[GetAsyncGenLogTasks] Get async gen log task failed: %d", errCode); + return errCode; + } + + uint32_t taskPrefixLen = std::string(ASYNC_GEN_LOG_TASK_PREFIX).size(); + for (const auto &task : std::as_const(asyncTaskMap)) { + std::string taskTableName; + DBCommon::VectorToString(task.second, taskTableName); + std::string taskName; + DBCommon::VectorToString(task.first, taskName); + if (taskName.size() < taskPrefixLen) { + LOGW("[GetAsyncGenLogTasks] Invalid async gen log task name: %s", taskName.c_str()); + continue; + } + std::string taskId = taskName.substr(taskPrefixLen); + if (!DBCommon::IsStringAllDigit(taskId)) { + LOGW("[GetAsyncGenLogTasks] Invalid async gen log task ID: %s", taskName.c_str()); + continue; + } + int curTaskId = std::strtol(taskId.c_str(), nullptr, DBConstant::STR_TO_LL_BY_DEVALUE); + asyncGenLogTasks.push_back(std::make_pair(curTaskId, std::string(taskTableName))); + } + std::sort(asyncGenLogTasks.begin(), asyncGenLogTasks.end()); + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::AddAsyncGenLogTask(const SQLiteSingleVerRelationalStorageExecutor *handle, + const std::string &tableName) +{ + std::vector> asyncGenLogTasks; + int errCode = GetAsyncGenLogTasks(handle, asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[AddAsyncGenLogTask] Get async gen log task failed: %d", errCode); + return errCode; + } + int curMaxTaskId = 0; + for (const auto &task : asyncGenLogTasks) { + curMaxTaskId = std::max(curMaxTaskId, task.first); + if (tableName == task.second) { + return E_OK; + } + } + curMaxTaskId++; + + Key curTaskKey; + DBCommon::StringToVector(std::string(ASYNC_GEN_LOG_TASK_PREFIX) + std::to_string(curMaxTaskId), curTaskKey); + Value curTaskVal; + DBCommon::StringToVector(tableName, curTaskVal); + errCode = handle->PutKvData(curTaskKey, curTaskVal); + if (errCode != E_OK) { + LOGE("[AddAsyncGenLogTask] Put async gen log task of table %s failed: %d", + DBCommon::StringMiddleMaskingWithLen(tableName).c_str(), errCode); + return errCode; + } + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::RUNNING) { + genLogTaskStatus_ = GenLogTaskStatus::RUNNING_APPENDED; + } + return errCode; +} + +int SQLiteSingleRelationalStorageEngine::GenCloudLogInfo(const std::string &identity) +{ + std::vector> asyncGenLogTasks; + bool isTaskAppend = false; + do { + asyncGenLogTasks.clear(); + int errCode = GetAsyncGenLogTasks(asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[GenCloudLogInfo] Get async gen log task failed: %d", errCode); + return errCode; + } + if (asyncGenLogTasks.empty()) { + break; + } + errCode = GenCloudLogInfoWithTables(identity, asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[GenCloudLogInfo] Generate cloud log failed: %d", errCode); + return errCode; + } + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::RUNNING_APPENDED) { + genLogTaskStatus_ = GenLogTaskStatus::RUNNING; + isTaskAppend = true; + } else if (genLogTaskStatus_ == GenLogTaskStatus::DB_CLOSED) { + return E_OK; + } else { + genLogTaskStatus_ = GenLogTaskStatus::IDLE; + } + } while (isTaskAppend); + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::GenCloudLogInfoWithTables(const std::string &identity, + const std::vector> &taskTables) +{ + int errCode = E_OK; + auto schema = GetSchema(); + for (const auto &[taskId, tableName] : taskTables) { + LOGI("[GenCloudLogInfoWithTables] start gen log of table %s", + DBCommon::StringMiddleMaskingWithLen(tableName).c_str()); + TableInfo table = schema.GetTable(tableName); + if (table.GetTableName().empty()) { + LOGW("[GenCloudLogInfoWithTables] gen log for no exist table, skip"); + continue; + } + auto mode = GetRelationalProperties().GetDistributedTableMode(); + std::unique_ptr tableManager = + LogTableManagerFactory::GetTableManager(table, mode, table.GetTableSyncType()); + errCode = GeneLogInfoForExistedDataInBatch(identity, table, tableManager); + if (errCode != E_OK) { + LOGE("[GenCloudLogInfoWithTables] Generate log of table %s for exist data failed: %d", + DBCommon::StringMiddleMaskingWithLen(tableName).c_str(), errCode); + return errCode; + } + errCode = RemoveAsyncGenLogTask(taskId); + if (errCode != E_OK) { + LOGW("[GenCloudLogInfoWithTables] Remove task of table %s for exist data failed: %d", + DBCommon::StringMiddleMaskingWithLen(tableName).c_str(), errCode); + } + { + std::unique_lock lock(genLogTaskCvMutex_); + genLogTaskCv_.notify_all(); + } + LOGI("[GenCloudLogInfoWithTables] finish gen log of table %s", + DBCommon::StringMiddleMaskingWithLen(tableName).c_str()); + } + return E_OK; +} + int SQLiteSingleRelationalStorageEngine::GenLogInfoForUpgrade(const std::string &tableName, bool schemaChanged) { int errCode = E_OK; @@ -1030,6 +1212,135 @@ int SQLiteSingleRelationalStorageEngine::GenLogInfoForUpgrade(const std::string return handle->Commit(); } +int SQLiteSingleRelationalStorageEngine::GeneLogInfoForExistedDataInBatch(const std::string &identity, + const TableInfo &tableInfo, std::unique_ptr &logMgrPtr) +{ + int changedCount = 0; + int batchNum = 0; + int recordBatchNum = 10; // print log when gen 10 batch + do { + if (IsNeedStopGenLogTask()) { + LOGI("gen log task interrupted."); + return -E_TASK_INTERRUPTED; + } + changedCount = 0; + int errCode = GeneLogInfoForExistedData(identity, tableInfo, logMgrPtr, + SQLiteRelationalUtils::BATCH_GEN_LOG_SIZE, changedCount); + if (errCode != E_OK) { + LOGE("[GeneLogInfoForExistedDataInBatch] Generate one batch log failed: %d", errCode); + return errCode; + } + batchNum++; + if (batchNum == recordBatchNum) { + LOGI("[GeneLogInfoForExistedDataInBatch] Generate 10 batch log finished"); + batchNum = 0; + } + std::this_thread::sleep_for(CloudDbConstant::ASYNC_GEN_LOG_INTERVAL); + } while (changedCount != 0); + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::GeneLogInfoForExistedData(const std::string &identity, + const TableInfo &tableInfo, std::unique_ptr &logMgrPtr, uint32_t limitNum, int &changedCount) +{ + int errCode = E_OK; + auto *handle = static_cast(FindExecutor(true, + OperatePerm::NORMAL_PERM, errCode)); + if (handle == nullptr) { + return errCode; + } + ResFinalizer finalizer([handle, this] { + SQLiteSingleVerRelationalStorageExecutor *releaseHandle = handle; + this->ReleaseExecutor(releaseHandle); + }); + errCode = handle->StartTransaction(TransactType::IMMEDIATE); + if (errCode != E_OK) { + return errCode; + } + auto transactionStart = std::chrono::steady_clock::now(); + sqlite3 *db = nullptr; + if (handle->GetDbHandle(db) != E_OK) { + LOGE("[GeneOneBatchLogInfoForExistedData] invalid db"); + (void)handle->Rollback(); + return -E_INVALID_DB; + } + SQLiteRelationalUtils::GenLogParam param = { + db, handle->IsMemory(), false, limitNum + }; + errCode = SQLiteRelationalUtils::GeneLogInfoForExistedData(identity, tableInfo, logMgrPtr, param); + if (errCode != E_OK) { + (void)handle->Rollback(); + LOGE("[GeneOneBatchLogInfoForExistedData] Generate one batch log failed: %d", errCode); + return errCode; + } + changedCount = sqlite3_changes(db); + errCode = handle->SetLogTriggerStatus(true); + if (errCode != E_OK) { + (void)handle->Rollback(); + return errCode; + } + auto duration = std::chrono::steady_clock::now() - transactionStart; + if (duration > CloudDbConstant::LONG_TIME_TRANSACTION) { + LOGI("[GeneOneBatchLogInfoForExistedData] Generate one batch log have cost %" PRId64 "ms.", duration.count()); + } + return handle->Commit(); +} + +int SQLiteSingleRelationalStorageEngine::GenCloudLogInfoIfNeeded(const std::string &tableName, + const std::string &identity) +{ + std::vector> asyncGenLogTasks; + int errCode = GetAsyncGenLogTasks(asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[GenCloudLogInfoIfNeeded] get async gen log tasks failed: %d", errCode); + return -E_INVALID_DB; + } + + for (const auto &[taskId, taskTableName] : asyncGenLogTasks) { + if (tableName != taskTableName) { + continue; + } + TableInfo tableInfo = GetSchema().GetTable(tableName); + auto mode = GetRelationalProperties().GetDistributedTableMode(); + std::unique_ptr tableManager = + LogTableManagerFactory::GetTableManager(tableInfo, mode, tableInfo.GetTableSyncType()); + int changedCount = 0; + errCode = GeneLogInfoForExistedData(identity, tableInfo, tableManager, 0u, changedCount); + if (errCode != E_OK) { + LOGE("[GenCloudLogInfoIfNeeded] gen log failed: %d, changedCount: %d", errCode, changedCount); + return errCode; + } + return RemoveAsyncGenLogTask(taskId); + } + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::RemoveAsyncGenLogTask(int taskId) +{ + int errCode = E_OK; + auto *handle = static_cast(FindExecutor(true, + OperatePerm::NORMAL_PERM, errCode)); + if (handle == nullptr) { + LOGE("[RemoveAsyncGenLogTask] Get handle failed to remove async gen log task: %d", errCode); + return errCode; + } + + Key curTaskKey; + DBCommon::StringToVector(std::string(ASYNC_GEN_LOG_TASK_PREFIX) + std::to_string(taskId), curTaskKey); + errCode = handle->DeleteMetaData({curTaskKey}); + ReleaseExecutor(handle); + if (errCode != E_OK) { + LOGE("[RemoveAsyncGenLogTask] Remove async gen log task %d failed: %d", taskId, errCode); + } + return errCode; +} + +bool SQLiteSingleRelationalStorageEngine::IsNeedStopGenLogTask() +{ + std::lock_guard lock(genLogTaskStatusMutex_); + return (genLogTaskStatus_ == GenLogTaskStatus::DB_CLOSED) || (genLogTaskStatus_ == GenLogTaskStatus::INTERRUPTED); +} + std::map> SQLiteSingleRelationalStorageEngine::GetReachableWithShared( const std::map> &reachableReference, const std::map &tableToShared) @@ -1209,6 +1520,178 @@ int SQLiteSingleRelationalStorageEngine::SetDistributedSchemaInTraction(Relation return errCode; } +void SQLiteSingleRelationalStorageEngine::StopGenLogTask(bool isCloseDb) +{ + if (isCloseDb) { + SetGenLogTaskStatus(GenLogTaskStatus::DB_CLOSED); + } else { + SetGenLogTaskStatus(GenLogTaskStatus::INTERRUPTED); + } +} + +int SQLiteSingleRelationalStorageEngine::StopGenLogTaskWithTables(const std::vector &tables) +{ + if (tables.empty()) { + StopGenLogTask(); + return E_OK; + } + std::vector> asyncGenLogTasks; + int errCode = GetAsyncGenLogTasks(asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[StopGenLogTaskWithTables] get async gen log tasks failed: %d", errCode); + return errCode; + } + std::set tableSet(tables.begin(), tables.end()); + for (const auto &[taskId, tableName] : asyncGenLogTasks) { + if (tableSet.count(tableName) > 0) { + LOGI("[StopGenLogTaskWithTables] exist table async gen log when stop task"); + StopGenLogTask(); + break; + } + } + return E_OK; +} + +void SQLiteSingleRelationalStorageEngine::ResetGenLogTaskStatus() +{ + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::INTERRUPTED) { + genLogTaskStatus_ = GenLogTaskStatus::IDLE; + } +} + +void SQLiteSingleRelationalStorageEngine::SetGenLogTaskStatus(GenLogTaskStatus status) +{ + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::DB_CLOSED) { + return; + } + genLogTaskStatus_ = status; +} + +int SQLiteSingleRelationalStorageEngine::WaitAsyncGenLogTaskFinished(const std::vector &tables, + const std::string &identity) +{ + int errCode = E_OK; + auto waitStart = std::chrono::steady_clock::now(); + std::chrono::microseconds duration(0); + do { + std::vector> asyncGenLogTasks; + if (IsNeedStopWaitGenLogTask(tables, asyncGenLogTasks, errCode)) { + return errCode; + } + bool isNeedStartGenLogTask = false; + { + std::lock_guard lock(genLogTaskStatusMutex_); + isNeedStartGenLogTask = (genLogTaskStatus_ == GenLogTaskStatus::IDLE); + if (isNeedStartGenLogTask) { + genLogTaskStatus_ = GenLogTaskStatus::RUNNING_BEFORE_SYNC; + } + } + if (isNeedStartGenLogTask) { + errCode = GenCloudLogInfoWithTables(identity, asyncGenLogTasks); + { + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::RUNNING_BEFORE_SYNC) { + genLogTaskStatus_ = GenLogTaskStatus::IDLE; + } + } + if (errCode != E_OK) { + LOGE("[WaitAsyncGenLogTaskFinished] Trigger async gen log task failed: %d", errCode); + return errCode; + } + std::unique_lock lock(genLogTaskCvMutex_); + genLogTaskCv_.notify_all(); + break; + } else { + std::unique_lock lock(genLogTaskCvMutex_); + genLogTaskCv_.wait_for(lock, SYNC_WAIT_GEN_LOG_ONCE_TIME); + duration = std::chrono::duration_cast(std::chrono::steady_clock::now() - + waitStart); + LOGI("[WaitAsyncGenLogTaskFinished] Waiting for the log generation task to finished before cloud sync."); + } + } while (duration < SYNC_WAIT_GEN_LOG_MAX_TIME); + if (duration >= SYNC_WAIT_GEN_LOG_MAX_TIME) { + LOGE("[WaitAsyncGenLogTaskFinished] Waiting for the log generation task timeout"); + return -E_TIMEOUT; + } + return E_OK; +} + +bool SQLiteSingleRelationalStorageEngine::IsNeedStopWaitGenLogTask(const std::vector &tables, + std::vector> &asyncGenLogTasks, int &errCode) +{ + if (IsNeedStopGenLogTask()) { + errCode = -E_TASK_INTERRUPTED; + return true; + } + std::set tableSet(tables.begin(), tables.end()); + errCode = GetAsyncGenLogTasksWithTables(tableSet, asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[WaitAsyncGenLogTaskFinished] Check async gen log task failed: %d", errCode); + return true; + } + return asyncGenLogTasks.empty(); +} + +int SQLiteSingleRelationalStorageEngine::GetAsyncGenLogTasksWithTables(const std::set &tables, + std::vector> &tasks) +{ + std::vector> asyncGenLogTasks; + int errCode = GetAsyncGenLogTasks(asyncGenLogTasks); + if (errCode != E_OK) { + LOGE("[CheckAsyncGenLogTasks] Get async gen log task failed: %d", errCode); + return errCode; + } + for (const auto &task : asyncGenLogTasks) { + if (tables.find(task.second) != tables.end()) { + tasks.push_back(task); + } + } + return E_OK; +} + +int SQLiteSingleRelationalStorageEngine::TriggerGenLogTask(const std::string &identity) +{ + RefObject::IncObjRef(this); + int errCode = RuntimeContext::GetInstance()->ScheduleTask([this, identity]() { + bool isNeedStartTask = false; + { + std::lock_guard lock(genLogTaskStatusMutex_); + if (genLogTaskStatus_ == GenLogTaskStatus::IDLE || + genLogTaskStatus_ == GenLogTaskStatus::RUNNING_BEFORE_SYNC) { + genLogTaskStatus_ = GenLogTaskStatus::RUNNING; + isNeedStartTask = true; + } + } + if (isNeedStartTask) { + int ret = GenCloudLogInfo(identity); + if (ret != E_OK && ret != -E_TASK_INTERRUPTED) { + LOGE("[TriggerGenLogTask] Gen cloud log info failed: %d", ret); + SetGenLogTaskStatus(GenLogTaskStatus::IDLE); + } + } + RefObject::DecObjRef(this); + }); + return errCode; +} + +std::pair SQLiteSingleRelationalStorageEngine::AnalyzeTable(const std::string &tableName) +{ + std::pair res; + auto &[errCode, _] = res; + auto *handle = static_cast(FindExecutor(false, OperatePerm::NORMAL_PERM, + errCode)); + if (handle == nullptr) { + return res; + } + ResFinalizer resFinalizer([this, handle]() { + auto rdbHandle = handle; + ReleaseExecutor(rdbHandle); + }); + return handle->AnalyzeTable(tableName); +} + #ifdef USE_DISTRIBUTEDDB_CLOUD int SQLiteSingleRelationalStorageEngine::PutCloudGid(const std::string &tableName, std::vector &data) { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h index 7e81d1182e7941c4aa5c137f7e36810a407a6fe4..f35cdc836716c7220506d38f92f79d7e44e92934 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h @@ -24,6 +24,34 @@ #include "tracker_table.h" namespace DistributedDB { +enum class GenLogTaskStatus { + IDLE = 0, + RUNNING, + RUNNING_BEFORE_SYNC, + RUNNING_APPENDED, + INTERRUPTED, + DB_CLOSED, +}; + +// Once waiting time for log generation task before cloud sync +constexpr std::chrono::milliseconds SYNC_WAIT_GEN_LOG_ONCE_TIME = std::chrono::milliseconds(10 * 1000); +// Max waiting time for log generation task before cloud sync +constexpr std::chrono::milliseconds SYNC_WAIT_GEN_LOG_MAX_TIME = std::chrono::milliseconds(20 * 60 * 1000); + +struct GenerateLogInfo { + std::string tableName; + std::string identity; +}; + +struct CreateDistributedTableParam { + bool isTrackerSchemaChanged = false; + TableSyncType syncType = TableSyncType::DEVICE_COOPERATION; + std::string tableName; + std::string identity; + std::optional cloudTable; + bool isAsync = false; +}; + class SQLiteSingleRelationalStorageEngine : public SQLiteStorageEngine { public: explicit SQLiteSingleRelationalStorageEngine(RelationalDBProperties properties); @@ -36,8 +64,7 @@ public: RelationalSchemaObject GetSchema() const; - int CreateDistributedTable(const std::string &tableName, const std::string &identity, bool &schemaChanged, - TableSyncType syncType, bool trackerSchemaChanged); + int CreateDistributedTable(const CreateDistributedTableParam ¶m, bool &schemaChanged); int CleanDistributedDeviceTable(std::vector &missingTables); @@ -70,6 +97,18 @@ public: std::pair SetDistributedSchema(const DistributedSchema &schema, const std::string &localIdentity, bool isForceUpgrade); + void ResetGenLogTaskStatus(); + void StopGenLogTask(bool isCloseDb = false); + int StopGenLogTaskWithTables(const std::vector &tables); + bool IsNeedStopWaitGenLogTask(const std::vector &tables, + std::vector> &asyncGenLogTasks, int &errCode); + int WaitAsyncGenLogTaskFinished(const std::vector &tables, const std::string &identity); + int GetAsyncGenLogTasks(std::vector> &asyncGenLogTasks); + int GetAsyncGenLogTasksWithTables(const std::set &tables, + std::vector> &tasks); + + std::pair AnalyzeTable(const std::string &tableName); + #ifdef USE_DISTRIBUTEDDB_CLOUD int PutCloudGid(const std::string &tableName, std::vector &data); int DropTempTable(const std::string &tableName); @@ -86,13 +125,13 @@ private: // For db. int RegisterFunction(sqlite3 *db) const; - int UpgradeDistributedTable(const std::string &tableName, bool &schemaChanged, TableSyncType syncType); + int UpgradeDistributedTable(const TableInfo &tableInfo, bool &schemaChanged, TableSyncType syncType); int CreateDistributedTable(SQLiteSingleVerRelationalStorageExecutor *&handle, bool isUpgraded, - const std::string &identity, TableInfo &table, RelationalSchemaObject &schema); + const CreateDistributedTableParam ¶m, TableInfo &table, RelationalSchemaObject &schema); - int CreateDistributedTable(const std::string &tableName, bool isUpgraded, const std::string &identity, - RelationalSchemaObject &schema, TableSyncType tableSyncType); + int CreateDistributedTable(const CreateDistributedTableParam ¶m, bool isUpgraded, + RelationalSchemaObject &schema); int CreateDistributedSharedTable(SQLiteSingleVerRelationalStorageExecutor *&handle, const std::string &tableName, const std::string &sharedTableName, TableSyncType syncType, RelationalSchemaObject &schema); @@ -100,7 +139,23 @@ private: int CleanTrackerDeviceTable(const std::vector &tableNames, RelationalSchemaObject &trackerSchemaObj, SQLiteSingleVerRelationalStorageExecutor *&handle); + void SetGenLogTaskStatus(GenLogTaskStatus status); + int GetAsyncGenLogTasks(const SQLiteSingleVerRelationalStorageExecutor *handle, + std::vector> &asyncGenLogTasks); + int AddAsyncGenLogTask(const SQLiteSingleVerRelationalStorageExecutor *handle, + const std::string &tableName); + int GenCloudLogInfo(const std::string &identity); + int GenCloudLogInfoWithTables(const std::string &identity, + const std::vector> &taskTables); + int GenCloudLogInfoIfNeeded(const std::string &tableName, const std::string &identity); int GenLogInfoForUpgrade(const std::string &tableName, bool schemaChanged); + int GeneLogInfoForExistedDataInBatch(const std::string &identity, const TableInfo &tableInfo, + std::unique_ptr &logMgrPtr); + int GeneLogInfoForExistedData(const std::string &identity, const TableInfo &tableInfo, + std::unique_ptr &logMgrPtr, uint32_t limitNum, int &changedCount); + int TriggerGenLogTask(const std::string &identity); + int RemoveAsyncGenLogTask(int taskId); + bool IsNeedStopGenLogTask(); static std::map> GetReachableWithShared( const std::map> &reachableReference, @@ -149,6 +204,11 @@ private: RelationalDBProperties properties_; std::mutex createDistributedTableMutex_; mutable std::mutex propertiesMutex_; + + mutable std::mutex genLogTaskStatusMutex_; + GenLogTaskStatus genLogTaskStatus_ = GenLogTaskStatus::IDLE; + mutable std::mutex genLogTaskCvMutex_; + std::condition_variable genLogTaskCv_; }; } // namespace DistributedDB #endif diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_continue_token.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_continue_token.cpp index f6081a64b26af6a8f86b00194b6df00feaac5e67..a804e6197c020bc8ff57c81996c009cff73fafd9 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_continue_token.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_continue_token.cpp @@ -189,6 +189,7 @@ int SQLiteSingleVerRelationalContinueToken::GetCloudStatement(sqlite3 *db, Cloud } std::string sql = helper.GetRelationalCloudQuerySql(tableSchema_.fields, cloudDataResult.isCloudForcePushStrategy, cloudDataResult.isCompensatedTask, cloudDataResult.mode); + helper.SetAppendCondition(!queryObj_.IsRelaxForDelete() || cloudDataResult.mode != CloudWaterType::DELETE); errCode = helper.GetCloudQueryStatement(true, db, sql, queryStmt_, true); if (errCode == E_OK) { queryStmt = queryStmt_; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp index a50b9218338bcbd0b28910785ea303df019f16cb..211a3b954aead31b313236e9351c7470782baa5a 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp @@ -158,7 +158,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GeneLogInfoForExistedData(sqlite3 return SQLiteRelationalUtils::GeneLogInfoForExistedData(identity, tableInfo, logMgrPtr, param); } -int SQLiteSingleVerRelationalStorageExecutor::ResetLogStatus(std::string &tableName) +int SQLiteSingleVerRelationalStorageExecutor::ResetLogStatus(const std::string &tableName) { int errCode = SetLogTriggerStatus(false); if (errCode != E_OK) { @@ -222,7 +222,7 @@ int SQLiteSingleVerRelationalStorageExecutor::UpdateTrackerTable(sqlite3 *db, co } int SQLiteSingleVerRelationalStorageExecutor::CreateRelationalLogTable(DistributedTableMode mode, bool isUpgraded, - const std::string &identity, TableInfo &table) + const std::string &identity, TableInfo &table, bool isAsync) { // create log table std::unique_ptr tableManager; @@ -236,6 +236,15 @@ int SQLiteSingleVerRelationalStorageExecutor::CreateRelationalLogTable(Distribut LOGE("[CreateDistributedTable] create log table failed"); return errCode; } + // add trigger + errCode = tableManager->AddRelationalLogTableTrigger(dbHandle_, table, identity); + if (errCode != E_OK) { + LOGE("[CreateDistributedTable] Add relational log table trigger failed."); + return errCode; + } + if (isAsync) { + return SetLogTriggerStatus(true); + } std::string tableName = table.GetTableName(); bool isOnceDropped = false; (void)IsTableOnceDropped(tableName, isOnceDropped); @@ -260,17 +269,11 @@ int SQLiteSingleVerRelationalStorageExecutor::CreateRelationalLogTable(Distribut return errCode; } - // add trigger - errCode = tableManager->AddRelationalLogTableTrigger(dbHandle_, table, identity); - if (errCode != E_OK) { - LOGE("[CreateDistributedTable] Add relational log table trigger failed."); - return errCode; - } return SetLogTriggerStatus(true); } int SQLiteSingleVerRelationalStorageExecutor::CreateDistributedTable(DistributedTableMode mode, bool isUpgraded, - const std::string &identity, TableInfo &table) + const std::string &identity, TableInfo &table, bool isAsync) { if (dbHandle_ == nullptr) { return -E_INVALID_DB; @@ -303,7 +306,7 @@ int SQLiteSingleVerRelationalStorageExecutor::CreateDistributedTable(Distributed return errCode; } - return CreateRelationalLogTable(mode, isUpgraded, identity, table); + return CreateRelationalLogTable(mode, isUpgraded, identity, table, isAsync); } int SQLiteSingleVerRelationalStorageExecutor::CompareSchemaTableColumns(const std::string &tableName) @@ -332,18 +335,20 @@ int SQLiteSingleVerRelationalStorageExecutor::CompareSchemaTableColumns(const st return errCode; } -int SQLiteSingleVerRelationalStorageExecutor::UpgradeDistributedTable(const std::string &tableName, +int SQLiteSingleVerRelationalStorageExecutor::UpgradeDistributedTable(const TableInfo &localTableInfo, DistributedTableMode mode, bool &schemaChanged, RelationalSchemaObject &schema, TableSyncType syncType) { if (dbHandle_ == nullptr) { return -E_INVALID_DB; } TableInfo newTableInfo; + auto tableName = localTableInfo.GetTableName(); int errCode = SQLiteUtils::AnalysisSchema(dbHandle_, tableName, newTableInfo); if (errCode != E_OK) { LOGE("[UpgradeDistributedTable] analysis table schema failed. %d", errCode); return errCode; } + newTableInfo.SetCloudTable(localTableInfo.GetCloudTable()); if (CheckTableConstraint(newTableInfo, mode, syncType) != E_OK) { LOGE("[UpgradeDistributedTable] Not support create distributed table when violate constraints."); return -E_NOT_SUPPORT; @@ -1602,7 +1607,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetCloudDataForSync(const CloudUpl return errCode; } SQLiteRelationalUtils::CalCloudValueLen(cloudValue, totalSize); - errCode = PutVBucketByType(data, tableSchema_.fields[cid], cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(data, tableSchema_.fields[cid], cloudValue); if (errCode != E_OK) { return errCode; } @@ -1874,7 +1879,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetUpdateLogRecordStatement(const return errCode; } } else { - updateLogSql += GetUpdateDataFlagSql(vBucket) + ", cloud_gid = ?"; + updateLogSql += GetUpdateDataFlagSql(opType, vBucket) + ", cloud_gid = ?"; updateColName.push_back(CloudDbConstant::GID_FIELD); CloudStorageUtils::AddUpdateColForShare(tableSchema, updateLogSql, updateColName); } @@ -1992,6 +1997,20 @@ int SQLiteSingleVerRelationalStorageExecutor::ConvertLogToLocal(const std::strin return CloudStorageUtils::ConvertLogToLocal(dbHandle_, tableName, gids); } +std::vector SQLiteSingleVerRelationalStorageExecutor::GetInsertFields(const VBucket &vBucket, + const TableSchema &tableSchema) const +{ + if (putDataMode_ == PutDataMode::SYNC) { + return SQLiteRelationalUtils::GetSaveSyncField(vBucket, tableSchema, true); + } + return tableSchema.fields; +} + +std::pair SQLiteSingleVerRelationalStorageExecutor::AnalyzeTable(const std::string &tableName) const +{ + return SQLiteRelationalUtils::AnalyzeTable(dbHandle_, tableName); +} + #ifdef USE_DISTRIBUTEDDB_CLOUD int SQLiteSingleVerRelationalStorageExecutor::PutCloudGid(const std::string &tableName, std::vector &data) const diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h index 3b1dd575490b6524b05628700d6bb17a5c3e2700..8894f9f1ee2204226a9a64a58de5d0413f2eda49 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h @@ -58,16 +58,16 @@ public: // Delete the copy and assign constructors DISABLE_COPY_ASSIGN_MOVE(SQLiteSingleVerRelationalStorageExecutor); - int ResetLogStatus(std::string &tableName); + int ResetLogStatus(const std::string &tableName); int CreateRelationalLogTable(DistributedTableMode mode, bool isUpgraded, const std::string &identity, - TableInfo &table); + TableInfo &table, bool isAsync); // The parameter "identity" is a hash string that identifies a device int CreateDistributedTable(DistributedTableMode mode, bool isUpgraded, const std::string &identity, - TableInfo &table); + TableInfo &table, bool isAsync); - int UpgradeDistributedTable(const std::string &tableName, DistributedTableMode mode, bool &schemaChanged, + int UpgradeDistributedTable(const TableInfo &localTableInfo, DistributedTableMode mode, bool &schemaChanged, RelationalSchemaObject &schema, TableSyncType syncType); int StartTransaction(TransactType type); @@ -262,10 +262,6 @@ public: int CleanDownloadingFlagByGid(const std::string &tableName, const std::string &gid, VBucket dbAssets); - void CheckAndCreateTrigger(const TableInfo &table); - - bool CheckNullExtendLog(const TrackerTable &table); - int GetLockStatusByGid(const std::string &tableName, const std::string &gid, LockStatus &status); int CompareSchemaTableColumns(const std::string &tableName); @@ -286,6 +282,14 @@ public: int ConvertLogToLocal(const std::string &tableName, const std::vector &gids); + int UpdateTrackerTable(sqlite3 *db, const std::string &identity, const TableInfo &tableInfo, + std::unique_ptr &logMgrPtr, bool isTimestampOnly); + + int GetLocalDataByRowid(const TableInfo &table, const TableSchema &tableSchema, + DataInfoWithLog &dataInfoWithLog) const; + + std::pair AnalyzeTable(const std::string &tableName) const; + #ifdef USE_DISTRIBUTEDDB_CLOUD int PutCloudGid(const std::string &tableName, std::vector &data) const; @@ -379,14 +383,12 @@ private: int GetCloudDataForSync(const CloudUploadRecorder &uploadRecorder, sqlite3_stmt *statement, CloudSyncData &cloudDataResult, uint32_t &stepNum, uint32_t &totalSize); - int PutVBucketByType(VBucket &vBucket, const Field &field, Type &cloudValue); - int GetDownloadAsset(std::vector &assetsV, const Field &field, Type &cloudValue); int ExecutePutCloudData(const std::string &tableName, const TableSchema &tableSchema, const TrackerTable &trackerTable, DownloadData &downloadData, std::map &statisticMap); - std::string GetInsertSqlForCloudSync(const TableSchema &tableSchema); + std::string GetInsertSqlForCloudSync(const std::vector &insertFields, const TableSchema &tableSchema); int GetPrimaryKeyHashValue(const VBucket &vBucket, const TableSchema &tableSchema, std::vector &hashValue, bool allowEmpty = false); @@ -439,7 +441,7 @@ private: int GetUpdateDataTableStatement(const VBucket &vBucket, const TableSchema &tableSchema, sqlite3_stmt *&updateStmt); - int UpdateCloudData(VBucket &vBucket, const TableSchema &tableSchema); + int UpdateCloudData(OpType op, const TableSchema &tableSchema, VBucket &vBucket); int GetUpdateLogRecordStatement(const TableSchema &tableSchema, const VBucket &vBucket, OpType opType, std::vector &updateColName, sqlite3_stmt *&updateLogStmt); @@ -454,9 +456,6 @@ private: int GetDeleteStatementForCloudSync(const TableSchema &tableSchema, const std::set &pkSet, const VBucket &vBucket, sqlite3_stmt *&deleteStmt); - int UpdateTrackerTable(sqlite3 *db, const std::string &identity, const TableInfo &tableInfo, - std::unique_ptr &logMgrPtr, bool isTimestampOnly); - int DeleteCloudData(const std::string &tableName, const VBucket &vBucket, const TableSchema &tableSchema, const TrackerTable &trackerTable); @@ -546,7 +545,7 @@ private: int64_t GetDataFlag(); - std::string GetUpdateDataFlagSql(const VBucket &data); + std::string GetUpdateDataFlagSql(OpType opType, const VBucket &data); std::string GetDev(); @@ -569,6 +568,8 @@ private: int RecoverNullExtendLogInner(const std::string &tableName, const std::set &extendColNames, const std::string &sql); + std::vector GetInsertFields(const VBucket &vBucket, const TableSchema &tableSchema) const; + int CleanTableTmpMsg(const std::vector &tableNameList); int UpdateHashKeyBeforePutCloudData(const DownloadData &downloadData, size_t index, @@ -605,6 +606,8 @@ private: std::atomic maxUploadCount_; std::atomic maxUploadSize_; + + std::atomic isGenLogStop_ = false; }; } // namespace DistributedDB #endif diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp index 41fa2acc2d0230a971fd1649aceb32db1c312ea7..3ae5bb3eab654ef03220d39ad04d51c49e78dcb2 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp @@ -119,7 +119,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetInfoByStatement(sqlite3_stmt *s if (errCode != E_OK) { break; } - errCode = PutVBucketByType(assetInfo, field, cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(assetInfo, field, cloudValue); if (errCode != E_OK) { break; } @@ -136,7 +136,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetInfoByStatement(sqlite3_stmt *s if (errCode != E_OK) { break; } - errCode = PutVBucketByType(dataInfoWithLog.primaryKeys, item.second, cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(dataInfoWithLog.primaryKeys, item.second, cloudValue); if (errCode != E_OK) { break; } @@ -144,15 +144,16 @@ int SQLiteSingleVerRelationalStorageExecutor::GetInfoByStatement(sqlite3_stmt *s return errCode; } -std::string SQLiteSingleVerRelationalStorageExecutor::GetInsertSqlForCloudSync(const TableSchema &tableSchema) +std::string SQLiteSingleVerRelationalStorageExecutor::GetInsertSqlForCloudSync(const std::vector &insertFields, + const TableSchema &tableSchema) { std::string sql = "insert into " + tableSchema.name + "("; - for (const auto &field : tableSchema.fields) { + for (const auto &field : insertFields) { sql += field.colName + ","; } sql.pop_back(); sql += ") values("; - for (size_t i = 0; i < tableSchema.fields.size(); i++) { + for (size_t i = 0; i < insertFields.size(); i++) { sql += "?,"; } sql.pop_back(); @@ -266,7 +267,8 @@ int SQLiteSingleVerRelationalStorageExecutor::ExecutePutCloudData(const std::str errCode = InsertCloudData(vBucket, tableSchema, trackerTable, GetLocalDataKey(index, downloadData)); break; case OpType::UPDATE: - errCode = UpdateCloudData(vBucket, tableSchema); + case OpType::INTEGRATE: + errCode = UpdateCloudData(op, tableSchema, vBucket); break; case OpType::DELETE: errCode = DeleteCloudData(tableName, vBucket, tableSchema, trackerTable); @@ -689,14 +691,15 @@ int SQLiteSingleVerRelationalStorageExecutor::PutCloudSyncData(const std::string LOGW("get cloud data count failed, %d", errCodeCount); } LOGI("save cloud data of table %s [length %zu]:%d, cloud data count:%lld, ins:%d, upd:%d, del:%d, only gid:%d," - "flag zero:%d, flag one:%d, upd timestamp:%d, clear gid:%d, not handle:%d, lock:%d", + "flag zero:%d, flag one:%d, upd timestamp:%d, clear gid:%d, not handle:%d, lock:%d, integrate:%d", DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size(), errCode, count, statisticMap[static_cast(OpType::INSERT)], statisticMap[static_cast(OpType::UPDATE)], statisticMap[static_cast(OpType::DELETE)], statisticMap[static_cast(OpType::ONLY_UPDATE_GID)], statisticMap[static_cast(OpType::SET_CLOUD_FORCE_PUSH_FLAG_ZERO)], statisticMap[static_cast(OpType::SET_CLOUD_FORCE_PUSH_FLAG_ONE)], statisticMap[static_cast(OpType::UPDATE_TIMESTAMP)], statisticMap[static_cast(OpType::CLEAR_GID)], - statisticMap[static_cast(OpType::NOT_HANDLE)], statisticMap[static_cast(OpType::LOCKED_NOT_HANDLE)]); + statisticMap[static_cast(OpType::NOT_HANDLE)], statisticMap[static_cast(OpType::LOCKED_NOT_HANDLE)], + statisticMap[static_cast(OpType::INTEGRATE)]); return errCode == E_OK ? ret : errCode; } @@ -735,7 +738,8 @@ int SQLiteSingleVerRelationalStorageExecutor::InsertCloudData(VBucket &vBucket, return errCode; } } - std::string sql = GetInsertSqlForCloudSync(tableSchema); + auto insertFields = GetInsertFields(vBucket, tableSchema); + std::string sql = GetInsertSqlForCloudSync(insertFields, tableSchema); sqlite3_stmt *insertStmt = nullptr; errCode = SQLiteUtils::GetStatement(dbHandle_, sql, insertStmt); if (errCode != E_OK) { @@ -746,7 +750,7 @@ int SQLiteSingleVerRelationalStorageExecutor::InsertCloudData(VBucket &vBucket, CloudStorageUtils::PrepareToFillAssetFromVBucket(vBucket, CloudStorageUtils::FillAssetBeforeDownload); } int ret = E_OK; - errCode = BindValueToUpsertStatement(vBucket, tableSchema.fields, insertStmt); + errCode = BindValueToUpsertStatement(vBucket, insertFields, insertStmt); if (errCode != E_OK) { SQLiteUtils::ResetStatement(insertStmt, true, ret); return errCode; @@ -1027,6 +1031,9 @@ int SQLiteSingleVerRelationalStorageExecutor::GetUpdateSqlForCloudSync(const std } std::string sql = "UPDATE " + tableSchema.name + " SET"; for (const auto &field : updateFields) { + if (field.dupCheckCol) { + continue; + } sql += " " + field.colName + " = ?,"; } sql.pop_back(); @@ -1085,7 +1092,8 @@ int SQLiteSingleVerRelationalStorageExecutor::GetUpdateDataTableStatement(const return errCode; } -int SQLiteSingleVerRelationalStorageExecutor::UpdateCloudData(VBucket &vBucket, const TableSchema &tableSchema) +int SQLiteSingleVerRelationalStorageExecutor::UpdateCloudData(OpType op, const TableSchema &tableSchema, + VBucket &vBucket) { if (putDataMode_ == PutDataMode::SYNC) { CloudStorageUtils::PrepareToFillAssetFromVBucket(vBucket, CloudStorageUtils::FillAssetBeforeDownload); @@ -1110,7 +1118,7 @@ int SQLiteSingleVerRelationalStorageExecutor::UpdateCloudData(VBucket &vBucket, SQLiteUtils::ResetStatement(updateStmt, true, ret); // update log - errCode = UpdateLogRecord(vBucket, tableSchema, OpType::UPDATE); + errCode = UpdateLogRecord(vBucket, tableSchema, op); if (errCode != E_OK) { LOGE("update log record failed when update cloud data, errCode = %d", errCode); } @@ -1639,6 +1647,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetAllUploadCount(const std::vecto std::string sql = helper.GetCountRelationalCloudQuerySql(isCloudForcePush, isCompensatedTask, typeVec[i]); int64_t tempCount = 0; helper.AppendCloudQueryToGetDiffData(sql, typeVec[i]); + helper.SetAppendCondition(!query.IsRelaxForDelete() || typeVec[i] != CloudWaterType::DELETE); errCode = GetUploadCountInner(timestampVec[i], helper, sql, tempCount); if (errCode != E_OK) { return errCode; @@ -1698,34 +1707,6 @@ int SQLiteSingleVerRelationalStorageExecutor::GetSyncCloudData(const CloudUpload return errCode; } -int SQLiteSingleVerRelationalStorageExecutor::PutVBucketByType(VBucket &vBucket, const Field &field, Type &cloudValue) -{ - if (field.type == TYPE_INDEX && cloudValue.index() == TYPE_INDEX) { - Asset asset; - int errCode = RuntimeContext::GetInstance()->BlobToAsset(std::get(cloudValue), asset); - if (errCode != E_OK) { - return errCode; - } - if (!CloudStorageUtils::CheckAssetStatus({asset})) { - return -E_CLOUD_ERROR; - } - vBucket.insert_or_assign(field.colName, asset); - } else if (field.type == TYPE_INDEX && cloudValue.index() == TYPE_INDEX) { - Assets assets; - int errCode = RuntimeContext::GetInstance()->BlobToAssets(std::get(cloudValue), assets); - if (errCode != E_OK) { - return errCode; - } - if (CloudStorageUtils::IsAssetsContainDuplicateAsset(assets) || !CloudStorageUtils::CheckAssetStatus(assets)) { - return -E_CLOUD_ERROR; - } - vBucket.insert_or_assign(field.colName, assets); - } else { - vBucket.insert_or_assign(field.colName, cloudValue); - } - return E_OK; -} - int SQLiteSingleVerRelationalStorageExecutor::GetDownloadAsset(std::vector &assetsV, const Field &field, Type &cloudValue) { @@ -1772,7 +1753,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetAssetInfoOnTable(sqlite3_stmt * if (errCode != E_OK) { break; } - errCode = PutVBucketByType(assetInfo, field, cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(assetInfo, field, cloudValue); if (errCode != E_OK) { break; } diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp index 6ed1ebe62d3541608cbce46fbf71b0b4b4a37a1c..072caae1bd3bde4ece781d9a679bb9e3e2e3a2d7 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp @@ -1306,7 +1306,7 @@ std::pair SQLiteSingleVerRelationalStorageExecutor::GetAssetsByGi if (errCode != E_OK) { break; } - errCode = PutVBucketByType(assets, field, cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(assets, field, cloudValue); if (errCode != E_OK) { break; } @@ -1705,8 +1705,11 @@ int64_t SQLiteSingleVerRelationalStorageExecutor::GetDataFlag() return static_cast(flag); } -std::string SQLiteSingleVerRelationalStorageExecutor::GetUpdateDataFlagSql(const VBucket &data) +std::string SQLiteSingleVerRelationalStorageExecutor::GetUpdateDataFlagSql(OpType opType, const VBucket &data) { + if (opType == OpType::INTEGRATE) { + return "flag = flag"; + } std::string retentionFlag = "flag = (flag & " + std::to_string(static_cast(LogInfoFlag::FLAG_DEVICE_CLOUD_INCONSISTENCY) | static_cast(LogInfoFlag::FLAG_ASSET_DOWNLOADING_FOR_ASYNC)) + @@ -1731,28 +1734,10 @@ std::string SQLiteSingleVerRelationalStorageExecutor::GetDev() std::vector SQLiteSingleVerRelationalStorageExecutor::GetUpdateField(const VBucket &vBucket, const TableSchema &tableSchema) { - std::set useFields; - std::vector fields; if (putDataMode_ == PutDataMode::SYNC) { - for (const auto &field : tableSchema.fields) { - useFields.insert(field.colName); - } - fields = tableSchema.fields; - } else { - for (const auto &field : vBucket) { - if (field.first.empty() || field.first[0] == '#') { - continue; - } - useFields.insert(field.first); - } - for (const auto &field : tableSchema.fields) { - if (useFields.find(field.colName) == useFields.end()) { - continue; - } - fields.push_back(field); - } + return SQLiteRelationalUtils::GetSaveSyncField(vBucket, tableSchema, false); } - return fields; + return SQLiteRelationalUtils::GetUserUpdateField(vBucket, tableSchema); } int SQLiteSingleVerRelationalStorageExecutor::UpdateRecordFlag(const std::string &tableName, const std::string &sql, @@ -1919,7 +1904,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetRecordFromStmt(sqlite3_stmt *st if (errCode != E_OK) { break; } - errCode = PutVBucketByType(record, field, cloudValue); + errCode = SQLiteRelationalUtils::PutVBucketByType(record, field, cloudValue); if (errCode != E_OK) { break; } @@ -1963,12 +1948,6 @@ int SQLiteSingleVerRelationalStorageExecutor::BindShareValueToInsertLogStatement return errCode; } -void SQLiteSingleVerRelationalStorageExecutor::CheckAndCreateTrigger(const TableInfo &table) -{ - auto tableManager = std::make_unique(); - tableManager->CheckAndCreateTrigger(dbHandle_, table, ""); -} - void SQLiteSingleVerRelationalStorageExecutor::ClearLogOfMismatchedData(const std::string &tableName) { std::string misDataKeys = SQLiteRelationalUtils::GetMismatchedDataKeys(dbHandle_, tableName); @@ -2023,6 +2002,15 @@ int SQLiteSingleVerRelationalStorageExecutor::RecoverNullExtendLogInner(const st return SQLiteRelationalUtils::ExecuteListAction(actions); } +int SQLiteSingleVerRelationalStorageExecutor::GetLocalDataByRowid(const TableInfo &table, + const TableSchema &tableSchema, DataInfoWithLog &dataInfoWithLog) const +{ + if (!dataInfoWithLog.isQueryLocalData || dataInfoWithLog.logInfo.dataKey == -1) { // -1 means local not exist + return E_OK; + } + return SQLiteRelationalUtils::GetLocalDataByRowid(dbHandle_, table, tableSchema, dataInfoWithLog); +} + int SQLiteSingleVerRelationalStorageExecutor::CleanTableTmpMsg(const std::vector &tableNameList) { int errCode = E_OK; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.cpp index dd4a1cdb8eeae0d463948f35a02e770e3a416401..170131172e3c56810d56b619b28ffd84a6c64946 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.cpp @@ -769,5 +769,10 @@ int SqliteCloudKvStore::GetLocalDataCount(const std::string &tableName, int &dat storageHandle_->RecycleStorageExecutor(handle); return errCode; } + +int SqliteCloudKvStore::WaitAsyncGenLogTaskFinished(const std::vector &tables) +{ + return E_OK; +} } #endif // USE_DISTRIBUTEDDB_CLOUD \ No newline at end of file diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.h b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.h index 17580c7cac424be7f90f63cbc92697e56d3a8b18..b2b17780175d64ec06d47dc9c92383018320c67a 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_cloud_kv_store.h @@ -122,6 +122,8 @@ public: std::pair> GetDownloadAssetRecords(const std::string &tableName, int64_t beginTime) override; + + int WaitAsyncGenLogTaskFinished(const std::vector &tables) override; private: std::pair GetTransactionDbHandleAndMemoryStatus(bool isWrite); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp index 674cfc21164e3af0cb47173426d1f936075689f7..63c41e19d810e9bae4329bb88920db403c0c37b4 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.cpp @@ -153,6 +153,7 @@ SqliteQueryHelper::SqliteQueryHelper(const QueryObjInfo &info) hasPrefixKey_(info.hasPrefixKey_), isNeedOrderbyKey_(false), isRelationalQuery_(info.isRelationalQuery_), + isAppendCondition_(true), sortType_(info.sortType_) {} @@ -575,6 +576,9 @@ int SqliteQueryHelper::BindTimeRange(sqlite3_stmt *&statement, int &index, uint6 int SqliteQueryHelper::BindObjNodes(sqlite3_stmt *&statement, int &index) const { + if (!isAppendCondition_) { + return E_OK; + } int errCode = E_OK; int ret = E_OK; for (const QueryObjNode &objNode : queryObjNodes_) { @@ -1274,7 +1278,9 @@ int SqliteQueryHelper::GetCloudQueryStatement(bool useTimestampAlias, sqlite3 *d LOGE("To query sql fail! errCode[%d]", errCode); return errCode; } - sql += querySql_; + if (isAppendCondition_) { + sql += querySql_; + } errCode = SQLiteUtils::GetStatement(dbHandle, sql, statement); if (errCode != E_OK) { LOGE("[Query] Get statement fail!"); @@ -1453,4 +1459,9 @@ void SqliteQueryHelper::AppendKvQueryObjectOnSql(std::string &sql) sql += " AND " + MapKeysInToSql(keys_.size()); } } + +void SqliteQueryHelper::SetAppendCondition(bool isAppendCondition) +{ + isAppendCondition_ = isAppendCondition; +} } \ No newline at end of file diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.h b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.h index 8448b223d6c3ce70c9f57c89c6bbdb80a0e48651..2aba37c9545c5d93cc234c0dfa5a0f91c9e96b0c 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_query_helper.h @@ -124,6 +124,8 @@ public: std::pair GetKvCloudQueryStmt(sqlite3 *db, bool forcePush, const CloudWaterType mode, int64_t timeStamp, const std::string &user); + void SetAppendCondition(bool isAppendCondition); + static std::string GetKvCloudQuerySql(bool countOnly, bool forcePush); static void AppendCloudQueryToGetDiffData(std::string &sql, const CloudWaterType mode, bool isKv = false); @@ -181,6 +183,7 @@ private: bool hasPrefixKey_; bool isNeedOrderbyKey_; // The tag field is used for prefix query filtering key sorting bool isRelationalQuery_; + bool isAppendCondition_; SortType sortType_ = SortType::NONE; }; } diff --git a/frameworks/libs/distributeddb/storage/src/storage_proxy.cpp b/frameworks/libs/distributeddb/storage/src/storage_proxy.cpp index 8272d60f399ecc38cd4b2152ea9d4e16e2f801ff..4ebcd7d3ad4ba4d002e3e88f9199819240f2562b 100644 --- a/frameworks/libs/distributeddb/storage/src/storage_proxy.cpp +++ b/frameworks/libs/distributeddb/storage/src/storage_proxy.cpp @@ -418,7 +418,7 @@ int StorageProxy::GetPrimaryColNamesWithAssetsFields(const TableName &tableName, return ret; } for (const auto &field : tableSchema.fields) { - if (field.primary) { + if (field.primary || field.dupCheckCol) { colNames.push_back(field.colName); } if (field.type == TYPE_INDEX || field.type == TYPE_INDEX) { @@ -876,6 +876,48 @@ void StorageProxy::FilterDownloadRecordNotFound(const std::string &tableName, Do (void)store_->ConvertLogToLocal(tableName, gids); } +void StorageProxy::FilterDownloadRecordNoneSchemaField(const std::string &tableName, DownloadData &downloadData) +{ + std::shared_ptr schema; + int errCode = GetCloudDbSchema(schema); + if (errCode != E_OK || schema == nullptr) { + LOGW("[StorageProxy] Not found database schema, errCode %d", errCode); + return; + } + auto find = std::find_if(schema->tables.begin(), schema->tables.end(), + [&tableName](const TableSchema &tableSchema) { + return DBCommon::CaseInsensitiveCompare(tableSchema.name, tableName); + }); + if (find == schema->tables.end()) { + LOGW("[StorageProxy] Not found table schema"); + return; + } + std::set fieldNames; + std::for_each(find->fields.begin(), find->fields.end(), [&fieldNames](const Field &field) { + fieldNames.insert(field.colName); + }); + for (auto &row : downloadData.data) { + for (auto data = row.begin(); data != row.end();) { + if (fieldNames.find(data->first) == fieldNames.end() && + !data->first.empty() && data->first.at(0) != '#') { + // remove no exists col + data = row.erase(data); + } else { + ++data; + } + } + } +} + +int StorageProxy::WaitAsyncGenLogTaskFinished(const std::vector &tables) const +{ + if (store_ == nullptr) { + LOGE("[WaitAsyncGenLogTaskFinished] store is null"); + return -E_INVALID_DB; + } + return store_->WaitAsyncGenLogTaskFinished(tables); +} + int StorageProxy::PutCloudGid(const std::string &tableName, std::vector &data) { std::shared_lock readLock(storeMutex_); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.cpp index 9ff9cc5d2893077e4d69da71e8a4e663f8458aa8..23a0647b518299827fd17516c3d92914e227ad51 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.cpp @@ -840,6 +840,18 @@ void CloudDBProxy::CancelDownload() } } +void CloudDBProxy::SetCloudConflictHandler(const std::shared_ptr &handler) +{ + std::unique_lock writeLock(handlerMutex_); + conflictHandler_ = handler; +} + +std::weak_ptr CloudDBProxy::GetCloudConflictHandler() +{ + std::shared_lock writeLock(handlerMutex_); + return std::weak_ptr(conflictHandler_); +} + int CloudDBProxy::QueryAllGid(const std::string &tableName, VBucket &extend, std::vector &data) { std::shared_lock readLock(cloudMutex_); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.h b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.h index 118fb2a57b0b3396a74493ea0bbc5f6913792fa7..9cb2891d438cd5a6eb8fac5ae0578a1cc4a203c5 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_db_proxy.h @@ -20,6 +20,7 @@ #include #include #include "cloud/cloud_db_types.h" +#include "cloud/icloud_conflict_handler.h" #include "cloud/icloud_db.h" #include "cloud/iAssetLoader.h" @@ -84,6 +85,10 @@ public: void CancelDownload(); + void SetCloudConflictHandler(const std::shared_ptr &handler); + + std::weak_ptr GetCloudConflictHandler(); + int QueryAllGid(const std::string &tableName, VBucket &extend, std::vector &data); static int GetInnerErrorCode(DBStatus status); @@ -208,6 +213,8 @@ protected: std::map> cloudDbs_; std::shared_ptr iAssetLoader_; std::atomic isDownloading_; + mutable std::shared_mutex handlerMutex_; + std::shared_ptr conflictHandler_; mutable std::mutex genVersionMutex_; GenerateCloudVersionCallback genVersionCallback_; diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.cpp index 50346ccaf1ce45f9945af100065087e7870a885d..578d3d5d4df1cdeb7b40ac1edd0ff6d870499a80 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.cpp @@ -17,9 +17,12 @@ #include "db_errno.h" namespace DistributedDB { int CloudLocker::BuildCloudLock(const AfterBuildAction &buildAction, const BeforeFinalize &finalize, - std::shared_ptr &locker) + bool lockCloud, std::shared_ptr &locker) { locker = std::make_shared(); + if (!lockCloud) { + return E_OK; + } int errCode = buildAction(); if (errCode != E_OK) { locker = nullptr; diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.h b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.h index 979a55ff5b97152ce7e1fca52096ec224d9f5729..5a37154826fbf0c5441be85917626a6598813169 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_locker.h @@ -23,7 +23,7 @@ using BeforeFinalize = std::function; class CloudLocker final { public: static int BuildCloudLock(const AfterBuildAction &buildAction, const BeforeFinalize &finalize, - std::shared_ptr &locker); + bool lockCloud, std::shared_ptr &locker); CloudLocker() = default; ~CloudLocker(); private: diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_utils.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_utils.cpp index 121d155d7776195f5e26823dc659a4b71a895c36..7a1a8b8bb5c3750552ef71165eda0c15fb699b6e 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_utils.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_utils.cpp @@ -174,7 +174,7 @@ int CloudSyncUtils::CheckParamValid(const std::vector &devices, SyncMo LOGE("[CloudSyncer] not support mode %d", static_cast(mode)); return -E_NOT_SUPPORT; } - if (mode < SyncMode::SYNC_MODE_PUSH_ONLY || mode > SyncMode::SYNC_MODE_CLOUD_FORCE_PULL) { + if (mode < SyncMode::SYNC_MODE_PUSH_ONLY || mode > SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL) { LOGE("[CloudSyncer] invalid mode %d", static_cast(mode)); return -E_INVALID_ARGS; } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp index f9c5197b47d6e19f6d0623fd6460015af1ceccbc..893aabbefd5601830a4d0906aa8bc5a033a00469 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp @@ -30,7 +30,6 @@ #include "runtime_context.h" #include "storage_proxy.h" #include "store_types.h" -#include "strategy_factory.h" #include "version.h" namespace DistributedDB { @@ -194,10 +193,12 @@ void CloudSyncer::DoSyncIfNeed() CancelBackgroundDownloadAssetsTaskIfNeed(); // do sync logic std::vector usersList; + std::vector tables; { std::lock_guard autoLock(dataLock_); usersList = cloudTaskInfos_[triggerTaskId].users; currentContext_.currentUserIndex = 0; + tables = cloudTaskInfos_[triggerTaskId].table; } int errCode = E_OK; if (usersList.empty()) { @@ -216,6 +217,12 @@ void CloudSyncer::DoSyncIfNeed() int CloudSyncer::DoSync(TaskId taskId) { + int errCode = WaitAsyncGenLogTaskFinished(taskId); + if (errCode != E_OK) { + SyncMachineDoFinished(); + LOGE("[CloudSyncer] Wait async gen log task finished failed: %d", errCode); + return errCode; + } std::lock_guard lock(syncMutex_); ResetCurrentTableUploadBatchIndex(); CloudTaskInfo taskInfo; @@ -231,9 +238,7 @@ int CloudSyncer::DoSync(TaskId taskId) bool isLockAction = IsLockInDownload(); { std::lock_guard autoLock(dataLock_); - if (currentContext_.strategy != nullptr) { - needUpload = currentContext_.strategy->JudgeUpload(); - } + needUpload = strategyProxy_.JudgeUpload(); // 1. if the locker is already exist, directly reuse the lock, no need do the first download // 2. if the task(resume task) is already be tagged need upload data, no need do the first download isNeedFirstDownload = (currentContext_.locker == nullptr) && (!currentContext_.isNeedUpload) && @@ -242,23 +247,10 @@ int CloudSyncer::DoSync(TaskId taskId) bool isFirstDownload = true; if (isNeedFirstDownload) { // do first download - int errCode = DoDownloadInNeed(taskInfo, needUpload, isFirstDownload); - SetTaskFailed(taskId, errCode); - if (errCode != E_OK) { - SyncMachineDoFinished(); - return errCode; - } - bool isActuallyNeedUpload = false; // whether the task actually has data to upload - { - std::lock_guard autoLock(dataLock_); - isActuallyNeedUpload = currentContext_.isNeedUpload; - } - if (!isActuallyNeedUpload) { - LOGI("[CloudSyncer] no table need upload!"); - SyncMachineDoFinished(); - return E_OK; + auto [ret, isFinished] = DoFirstDownload(taskId, taskInfo, needUpload, isNeedFirstDownload); + if (isFinished) { + return ret; } - isFirstDownload = false; } { @@ -269,6 +261,35 @@ int CloudSyncer::DoSync(TaskId taskId) return DoSyncInner(taskInfo); } +std::pair CloudSyncer::DoFirstDownload(TaskId taskId, const CloudTaskInfo &taskInfo, bool needUpload, + bool &isFirstDownload) +{ + std::pair res; + auto &[errCode, isFinished] = res; + isFinished = false; + errCode = DoDownloadInNeed(taskInfo, needUpload, isFirstDownload); + SetTaskFailed(taskId, errCode); + if (errCode != E_OK) { + isFinished = true; + SyncMachineDoFinished(); + LOGE("[CloudSyncer] first download failed[%d]", errCode); + return res; + } + bool isActuallyNeedUpload = false; // whether the task actually has data to upload + { + std::lock_guard autoLock(dataLock_); + isActuallyNeedUpload = currentContext_.isNeedUpload; + } + if (!isActuallyNeedUpload) { + LOGI("[CloudSyncer] no table need upload!"); + SyncMachineDoFinished(); + isFinished = true; + return res; + } + isFirstDownload = false; + return res; +} + int CloudSyncer::PrepareAndUpload(const CloudTaskInfo &taskInfo, size_t index) { { @@ -799,8 +820,6 @@ int CloudSyncer::SaveDatum(SyncParam ¶m, size_t idx, std::vectorCheckSchema(tableName); if (ret != E_OK) { LOGE("[CloudSyncer] A schema error occurred on the table to be synced, %d", ret); @@ -958,7 +973,7 @@ void CloudSyncer::NotifyInDownload(CloudSyncer::TaskId taskId, SyncParam ¶m, return; } std::lock_guard autoLock(dataLock_); - if (currentContext_.strategy->JudgeUpload()) { + if (strategyProxy_.JudgeUpload()) { currentContext_.notifier->NotifyProcess(cloudTaskInfos_[taskId], param.info); } else { if (param.isLastBatch) { @@ -1127,7 +1142,7 @@ int CloudSyncer::DoDownload(CloudSyncer::TaskId taskId, bool isFirstDownload) void CloudSyncer::NotifyInEmptyDownload(CloudSyncer::TaskId taskId, InnerProcessInfo &info) { std::lock_guard autoLock(dataLock_); - if (currentContext_.strategy->JudgeUpload()) { + if (strategyProxy_.JudgeUpload()) { currentContext_.notifier->NotifyProcess(cloudTaskInfos_[taskId], info); } else { info.tableStatus = FINISHED; @@ -1141,25 +1156,7 @@ void CloudSyncer::NotifyInEmptyDownload(CloudSyncer::TaskId taskId, InnerProcess int CloudSyncer::PreCheckUpload(CloudSyncer::TaskId &taskId, const TableName &tableName, Timestamp &localMark) { - int ret = PreCheck(taskId, tableName); - if (ret != E_OK) { - return ret; - } - { - std::lock_guard autoLock(dataLock_); - if (cloudTaskInfos_.find(taskId) == cloudTaskInfos_.end()) { - LOGE("[CloudSyncer] Cloud Task Info does not exist taskId: %" PRIu64 ".", taskId); - return -E_INVALID_ARGS; - } - if ((cloudTaskInfos_[taskId].mode < SYNC_MODE_CLOUD_MERGE) || - (cloudTaskInfos_[taskId].mode > SYNC_MODE_CLOUD_FORCE_PUSH)) { - LOGE("[CloudSyncer] Upload failed, invalid sync mode: %d.", - static_cast(cloudTaskInfos_[taskId].mode)); - return -E_INVALID_ARGS; - } - } - - return ret; + return PreCheck(taskId, tableName); } int CloudSyncer::SaveUploadData(Info &insertInfo, Info &updateInfo, Info &deleteInfo, CloudSyncData &uploadData, @@ -1330,7 +1327,7 @@ int CloudSyncer::SaveCloudWaterMark(const TableName &tableName, const TaskId tas return E_OK; } cloudWaterMark = currentContext_.cloudWaterMarks[currentContext_.currentUserIndex][tableName]; - isUpdateCloudCursor = currentContext_.strategy->JudgeUpdateCursor(); + isUpdateCloudCursor = strategyProxy_.JudgeUpdateCursor(); } isUpdateCloudCursor = isUpdateCloudCursor && !(IsPriorityTask(taskId) && !IsNeedProcessCloudCursor(taskId)); if (isUpdateCloudCursor) { @@ -1415,6 +1412,7 @@ int CloudSyncer::QueryCloudData(TaskId taskId, const std::string &tableName, std } ret = cloudDB_.Query(tableName, extend, downloadData.data); storageProxy_->FilterDownloadRecordNotFound(tableName, downloadData); + storageProxy_->FilterDownloadRecordNoneSchemaField(tableName, downloadData); if ((ret == E_OK || ret == -E_QUERY_END) && downloadData.data.empty()) { if (extend[CloudDbConstant::CURSOR_FIELD].index() != TYPE_INDEX) { LOGE("[CloudSyncer] cursor type is not valid=%d", extend[CloudDbConstant::CURSOR_FIELD].index()); @@ -1515,12 +1513,12 @@ int CloudSyncer::PrepareSync(TaskId taskId) currentContext_.locker = tempLocker; } else { currentContext_.notifier = std::make_shared(this); - currentContext_.strategy = - StrategyFactory::BuildSyncStrategy(cloudTaskInfos_[taskId].mode, isKvScene_, policy_); currentContext_.notifier->Init(cloudTaskInfos_[taskId].table, cloudTaskInfos_[taskId].devices, cloudTaskInfos_[taskId].users); currentContext_.processRecorder = std::make_shared(); } + strategyProxy_.UpdateStrategy(cloudTaskInfos_[taskId].mode, isKvScene_, policy_, + cloudDB_.GetCloudConflictHandler()); LOGI("[CloudSyncer] exec storeId %.3s taskId %" PRIu64 " priority[%d] compensated[%d] logicDelete[%d]", cloudTaskInfos_[taskId].storeId.c_str(), taskId, static_cast(cloudTaskInfos_[taskId].priorityTask), static_cast(cloudTaskInfos_[taskId].compensatedTask), @@ -1739,35 +1737,10 @@ std::string CloudSyncer::GetIdentify() const return id_; } -int CloudSyncer::TagStatusByStrategy(bool isExist, SyncParam ¶m, DataInfo &dataInfo, OpType &strategyOpResult) -{ - strategyOpResult = OpType::NOT_HANDLE; - // ignore same record with local generate data - if (dataInfo.localInfo.logInfo.device.empty() && - !CloudSyncUtils::NeedSaveData(dataInfo.localInfo.logInfo, dataInfo.cloudLogInfo)) { - // not handle same data - return E_OK; - } - { - std::lock_guard autoLock(dataLock_); - if (!currentContext_.strategy) { - LOGE("[CloudSyncer] strategy has not been set when tag status, %d.", -E_INTERNAL_ERROR); - return -E_INTERNAL_ERROR; - } - bool isCloudWin = storageProxy_->IsTagCloudUpdateLocal(dataInfo.localInfo.logInfo, - dataInfo.cloudLogInfo, policy_); - strategyOpResult = currentContext_.strategy->TagSyncDataStatus(isExist, isCloudWin, - dataInfo.localInfo.logInfo, dataInfo.cloudLogInfo); - } - if (strategyOpResult == OpType::DELETE) { - param.deletePrimaryKeySet.insert(dataInfo.localInfo.logInfo.hashKey); - } - return E_OK; -} - int CloudSyncer::GetLocalInfo(size_t index, SyncParam ¶m, DataInfoWithLog &logInfo, std::map &localLogInfoCache, VBucket &localAssetInfo) { + logInfo.isQueryLocalData = strategyProxy_.JudgeQueryLocalData(); int errCode = storageProxy_->GetInfoByPrimaryKeyOrGid(param.tableName, param.downloadData.data[index], true, logInfo, localAssetInfo); if (errCode != E_OK && errCode != -E_NOT_FOUND) { @@ -1842,12 +1815,14 @@ void CloudSyncer::SetCurrentTaskFailedWithoutLock(int errCode) int CloudSyncer::LockCloudIfNeed(TaskId taskId) { + bool lockCloud = false; { std::lock_guard autoLock(dataLock_); if (currentContext_.locker != nullptr) { LOGD("[CloudSyncer] lock exist"); return E_OK; } + lockCloud = strategyProxy_.JudgeLocker(); } std::shared_ptr locker = nullptr; int errCode = CloudLocker::BuildCloudLock([taskId, this]() { @@ -1857,7 +1832,7 @@ int CloudSyncer::LockCloudIfNeed(TaskId taskId) if (unlockCode != E_OK) { SetCurrentTaskFailedWithoutLock(unlockCode); } - }, locker); + }, lockCloud, locker); { std::lock_guard autoLock(dataLock_); currentContext_.locker = locker; @@ -1883,7 +1858,7 @@ void CloudSyncer::ClearCurrentContextWithoutLock() failedHeartbeatCount_.erase(currentContext_.currentTaskId); currentContext_.currentTaskId = INVALID_TASK_ID; currentContext_.notifier = nullptr; - currentContext_.strategy = nullptr; + strategyProxy_.ResetStrategy(); currentContext_.processRecorder = nullptr; currentContext_.tableName.clear(); currentContext_.assetDownloadList.clear(); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h index 7c1f7baa0d6e2daa53f36070f311d3fb66c3f679..8bedcd484e3e82e815b16fda4bfd43d8518026df 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h @@ -23,11 +23,11 @@ #include "cloud/cloud_db_proxy.h" #include "cloud/cloud_store_types.h" #include "cloud/cloud_sync_state_machine.h" -#include "cloud/cloud_sync_strategy.h" #include "cloud/icloud_db.h" #include "cloud/icloud_syncer.h" #include "cloud/process_notifier.h" #include "cloud/process_recorder.h" +#include "cloud/strategy_proxy.h" #include "cloud_locker.h" #include "data_transformer.h" #include "db_common.h" @@ -62,9 +62,7 @@ public: int ClearCloudWatermark(const std::vector &tableNameList); - int StopSyncTask(const std::function &removeFunc); - - int StopTaskBeforeSetReference(std::function &setReferenceFunc); + int StopSyncTask(const std::function &removeFunc, int errCode = -E_CLOUD_ERROR); int CleanWaterMarkInMemory(const std::set &tableNameList); @@ -97,26 +95,27 @@ public: SyncProcess GetCloudTaskStatus(uint64_t taskId) const; int ClearCloudWatermark(std::function &clearFunc); + + void SetCloudConflictHandler(const std::shared_ptr &handler); protected: struct TaskContext { TaskId currentTaskId = 0u; + bool isNeedUpload = false; // whether the current task need to do upload + bool isRealNeedUpload = false; + bool isFirstDownload = false; + int currentUserIndex = 0; + int repeatCount = 0; std::string tableName; std::shared_ptr notifier; - std::shared_ptr strategy; std::shared_ptr processRecorder; std::map> assetFields; - // should be cleared after each Download - DownloadList assetDownloadList; // store GID and assets, using in upload procedure std::map>> assetsInfo; // struct: > std::map> cloudWaterMarks; std::shared_ptr locker; - bool isNeedUpload = false; // whether the current task need to do upload - bool isRealNeedUpload = false; - bool isFirstDownload = false; - int currentUserIndex = 0; - int repeatCount = 0; + // should be cleared after each Download + DownloadList assetDownloadList; }; struct UploadParam { int64_t count = 0; @@ -157,6 +156,9 @@ protected: int DoSync(TaskId taskId); + std::pair DoFirstDownload(TaskId taskId, const CloudTaskInfo &taskInfo, bool needUpload, + bool &isFirstDownload); + int PrepareAndUpload(const CloudTaskInfo &taskInfo, size_t index); int DoSyncInner(const CloudTaskInfo &taskInfo); @@ -170,7 +172,7 @@ protected: void UpdateProcessInfoWithoutUpload(CloudSyncer::TaskId taskId, const std::string &tableName, bool needNotify); - virtual int DoDownloadInNeed(const CloudTaskInfo &taskInfo, const bool needUpload, bool isFirstDownload); + virtual int DoDownloadInNeed(const CloudTaskInfo &taskInfo, bool needUpload, bool isFirstDownload); void SetNeedUpload(bool isNeedUpload); @@ -312,8 +314,6 @@ protected: void UpdateCloudWaterMark(TaskId taskId, const SyncParam ¶m); - int TagStatusByStrategy(bool isExist, SyncParam ¶m, DataInfo &dataInfo, OpType &strategyOpResult); - int CommitDownloadResult(const DownloadItem &downloadItem, InnerProcessInfo &info, DownloadCommitList &commitList, int errCode); @@ -452,7 +452,7 @@ protected: int UploadVersionRecordIfNeed(const UploadParam &uploadParam); - std::vector CopyAndClearTaskInfos(); + std::vector CopyAndClearTaskInfos(const std::optional taskId = {}); void WaitCurTaskFinished(); @@ -564,6 +564,10 @@ protected: void ExecuteHeartBeatTask(TaskId taskId); + int WaitAsyncGenLogTaskFinished(TaskId triggerTaskId); + + void RetainCurrentTaskInfo(TaskId taskId); + void SetCurrentTmpError(int errCode); int DoUpdateExpiredCursor(TaskId taskId, const std::string &table, std::string &newCursor); @@ -595,6 +599,7 @@ protected: std::mutex syncMutex_; // Clean Cloud Data and Sync are mutually exclusive CloudDBProxy cloudDB_; + StrategyProxy strategyProxy_; std::shared_ptr storageProxy_; std::atomic queuedManualSyncLimit_; diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend.cpp index 9f40b10b8472fc9a17e5bc84729a883cd5dc89a8..609f3cdf727748819fc6650579cacb17033c75b3 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend.cpp @@ -28,7 +28,6 @@ #include "log_print.h" #include "runtime_context.h" #include "store_types.h" -#include "strategy_factory.h" #include "version.h" namespace DistributedDB { @@ -131,14 +130,18 @@ int CloudSyncer::GetCloudGid( QuerySyncObject CloudSyncer::GetQuerySyncObject(const std::string &tableName) { std::lock_guard autoLock(dataLock_); + bool isCustomPush = cloudTaskInfos_[currentContext_.currentTaskId].mode == SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH; for (const auto &item : cloudTaskInfos_[currentContext_.currentTaskId].queryList) { if (item.GetTableName() == tableName) { - return item; + QuerySyncObject tmp = item; + tmp.SetRelaxForDelete(isCustomPush); + return tmp; } } LOGW("[CloudSyncer] not found query in cache"); QuerySyncObject querySyncObject; querySyncObject.SetTableName(tableName); + querySyncObject.SetRelaxForDelete(isCustomPush); return querySyncObject; } @@ -667,6 +670,12 @@ int CloudSyncer::SaveCursorIfNeed(const std::string &tableName) int CloudSyncer::PrepareAndDownload(const std::string &table, const CloudTaskInfo &taskInfo, bool isFirstDownload) { + { + std::lock_guard autoLock(dataLock_); + if (!strategyProxy_.JudgeDownload()) { + return E_OK; + } + } std::string hashDev; int errCode = RuntimeContext::GetInstance()->GetLocalIdentity(hashDev); if (errCode != E_OK) { @@ -836,7 +845,7 @@ void CloudSyncer::SetNeedUpload(bool isNeedUpload) currentContext_.isNeedUpload = isNeedUpload; } -int CloudSyncer::DoDownloadInNeed(const CloudTaskInfo &taskInfo, const bool needUpload, bool isFirstDownload) +int CloudSyncer::DoDownloadInNeed(const CloudTaskInfo &taskInfo, bool needUpload, bool isFirstDownload) { std::vector needNotifyTables; for (size_t i = 0; i < taskInfo.table.size(); ++i) { @@ -1412,7 +1421,7 @@ std::string CloudSyncer::GetStoreIdByTask(TaskId taskId) return cloudTaskInfos_[taskId].storeId; } -int CloudSyncer::StopSyncTask(const std::function &removeFunc) +int CloudSyncer::StopSyncTask(const std::function &removeFunc, int errCode) { hasKvRemoveTask = true; CloudSyncer::TaskId currentTask; @@ -1422,9 +1431,9 @@ int CloudSyncer::StopSyncTask(const std::function &removeFunc) currentTask = currentContext_.currentTaskId; } if (currentTask != INVALID_TASK_ID || asyncTaskId_ != INVALID_TASK_ID) { - StopAllTasks(-E_CLOUD_ERROR); + StopAllTasks(errCode); } - int errCode = E_OK; + errCode = E_OK; if (removeFunc != nullptr) { std::lock_guard lock(syncMutex_); errCode = removeFunc(); @@ -1445,10 +1454,14 @@ void CloudSyncer::StopAllTasks(int errCode) } // mark current task user_change SetTaskFailed(currentTask, errCode); - UnlockIfNeed(); - WaitCurTaskFinished(); - - std::vector infoList = CopyAndClearTaskInfos(); + std::optional currentTaskId; + if (errCode != -E_TASK_INTERRUPTED) { + UnlockIfNeed(); + WaitCurTaskFinished(); + } else { + currentTaskId = GetCurrentTaskId(); + } + std::vector infoList = CopyAndClearTaskInfos(currentTaskId); for (auto &info: infoList) { LOGI("[CloudSyncer] finished taskId %" PRIu64 " with errCode %d, isPriority %d.", info.taskId, errCode, info.priorityTask); @@ -1462,12 +1475,15 @@ void CloudSyncer::StopAllTasks(int errCode) int CloudSyncer::TagStatus(bool isExist, SyncParam ¶m, size_t idx, DataInfo &dataInfo, VBucket &localAssetInfo) { - OpType strategyOpResult = OpType::NOT_HANDLE; - int errCode = TagStatusByStrategy(isExist, param, dataInfo, strategyOpResult); + std::pair res; + { + std::lock_guard autoLock(dataLock_); + res = strategyProxy_.TagStatus(isExist, idx, storageProxy_, param, dataInfo); + } + const auto &[errCode, strategyOpResult] = res; if (errCode != E_OK) { return errCode; } - param.downloadData.opType[idx] = strategyOpResult; if (!IsDataContainAssets()) { return E_OK; } @@ -2094,17 +2110,24 @@ CloudSyncer::InnerProcessInfo CloudSyncer::GetInnerProcessInfo(const std::string return info; } -std::vector CloudSyncer::CopyAndClearTaskInfos() +std::vector CloudSyncer::CopyAndClearTaskInfos(const std::optional taskId) { std::vector infoList; std::lock_guard autoLock(dataLock_); - for (const auto &item: std::as_const(cloudTaskInfos_)) { - infoList.push_back(item.second); + for (const auto& [infoTaskId, info]: cloudTaskInfos_) { + if (!taskId.has_value() || infoTaskId != taskId.value()) { + infoList.push_back(info); + } + } + + if (taskId.has_value()) { + RetainCurrentTaskInfo(taskId.value()); + } else { + taskQueue_.clear(); + cloudTaskInfos_.clear(); + resumeTaskInfos_.clear(); + currentContext_.notifier = nullptr; } - taskQueue_.clear(); - cloudTaskInfos_.clear(); - resumeTaskInfos_.clear(); - currentContext_.notifier = nullptr; return infoList; } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend_extend.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend_extend.cpp index d6e2456b3f56d9b823a3edd148bddc41f9920935..d7506c5078dbeb26d4015dabc6d526578cb0a780 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend_extend.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer_extend_extend.cpp @@ -29,7 +29,6 @@ #include "res_finalizer.h" #include "runtime_context.h" #include "store_types.h" -#include "strategy_factory.h" #include "version.h" namespace DistributedDB { @@ -172,6 +171,58 @@ void CloudSyncer::ExecuteHeartBeatTask(TaskId taskId) DecObjRef(this); } +void CloudSyncer::SetCloudConflictHandler(const std::shared_ptr &handler) +{ + cloudDB_.SetCloudConflictHandler(handler); +} + +int CloudSyncer::WaitAsyncGenLogTaskFinished(TaskId triggerTaskId) +{ + if (storageProxy_ == nullptr) { + LOGE("[WaitAsyncGenLogTaskFinished] Invalid storage."); + return -E_INVALID_DB; + } + std::vector tables; + { + std::lock_guard autoLock(dataLock_); + if (cloudTaskInfos_.find(triggerTaskId) == cloudTaskInfos_.end()) { + LOGW("[WaitAsyncGenLogTaskFinished] Abort wait because of invalid task id"); + return -E_INVALID_DB; + } + tables = cloudTaskInfos_[triggerTaskId].table; + } + return storageProxy_->WaitAsyncGenLogTaskFinished(tables); +} + +void CloudSyncer::RetainCurrentTaskInfo(TaskId taskId) +{ + std::multimap> retainQueue; + for (const auto &kv : std::as_const(taskQueue_)) { + if (kv.second == taskId) { + retainQueue.emplace(kv.first, kv.second); + break; + } + } + taskQueue_ = std::move(retainQueue); + // clear task info and retain current taskinfo + auto cloudTaskIter = cloudTaskInfos_.find(taskId); + if (cloudTaskIter != cloudTaskInfos_.end()) { + const CloudTaskInfo cloudTaskInfo = cloudTaskIter->second; + cloudTaskInfos_.clear(); + cloudTaskInfos_.emplace(taskId, cloudTaskInfo); + } else { + cloudTaskInfos_.clear(); + } + auto resumeTaskIter = resumeTaskInfos_.find(taskId); + if (resumeTaskIter != resumeTaskInfos_.end()) { + const ResumeTaskInfo resumeTaskInfo = resumeTaskIter->second; + resumeTaskInfos_.clear(); + resumeTaskInfos_.emplace(taskId, resumeTaskInfo); + } else { + resumeTaskInfos_.clear(); + } +} + void CloudSyncer::SetCurrentTmpError(int errCode) { std::lock_guard guard(dataLock_); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e67e9210c1a4c689abd60fe463e673a45aed66c7 --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_custom_pull_strategy.h" + +namespace DistributedDB { +OpType CloudCustomPullStrategy::TagSyncDataStatus(const DataStatusInfo &statusInfo, const LogInfo &localInfo, + const VBucket &localData, const LogInfo &cloudInfo, VBucket &cloudData) const +{ + std::weak_ptr handler; + { + std::lock_guard autoLock(handlerMutex_); + handler = conflictHandler_; + } + auto conflictHandler = handler.lock(); + if (conflictHandler == nullptr) { + LOGW("[CloudCustomPullStrategy] Tag data status without conflict handler"); + return OpType::NOT_HANDLE; + } + VBucket upsert; + auto ret = conflictHandler->HandleConflict(statusInfo.table, localData, cloudData, upsert); + for (const auto &item: upsert) { + cloudData.insert_or_assign(item.first, item.second); + } + if (ret != ConflictRet::NOT_HANDLE) { + return ConvertToOpType(statusInfo.isExistInLocal, ret); + } + if (IsDelete(cloudInfo)) { + return OpType::CLEAR_GID; + } + if (localInfo.cloudGid.empty() || IsLogNeedUpdate(cloudInfo, localInfo)) { + return OpType::ONLY_UPDATE_GID; + } + return OpType::NOT_HANDLE; +} + +bool CloudCustomPullStrategy::JudgeUpdateCursor() const +{ + return true; +} + +bool CloudCustomPullStrategy::JudgeUpload() const +{ + return false; +} + +bool CloudCustomPullStrategy::JudgeLocker() const +{ + return false; +} + +bool CloudCustomPullStrategy::JudgeQueryLocalData() const +{ + return true; +} + +void CloudCustomPullStrategy::SetCloudConflictHandler(const std::weak_ptr &handler) +{ + std::lock_guard autoLock(handlerMutex_); + conflictHandler_ = handler; +} + +OpType CloudCustomPullStrategy::ConvertToOpType(bool isLocalExist, ConflictRet conflictRet) +{ + switch (conflictRet) { + case ConflictRet::UPSERT: + return isLocalExist ? OpType::UPDATE : OpType::INSERT; + case ConflictRet::INTEGRATE: + return isLocalExist ? OpType::INTEGRATE : OpType::INSERT; + case ConflictRet::DELETE: + return OpType::DELETE; + default: + LOGW("[CloudCustomPullStrategy] Unknown conflict ret %d", static_cast(conflictRet)); + return OpType::NOT_HANDLE; + } +} +} // DistributedDB \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.h new file mode 100644 index 0000000000000000000000000000000000000000..0c2a2da92a1122b1d52b24b70c37b5d3e6b1dc37 --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_pull_strategy.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_CUSTOM_PULL_STRATEGY_H +#define CLOUD_CUSTOM_PULL_STRATEGY_H + +#include "cloud_sync_strategy.h" + +namespace DistributedDB { +class CloudCustomPullStrategy : public CloudSyncStrategy { +public: + OpType TagSyncDataStatus(const DataStatusInfo &statusInfo, const LogInfo &localInfo, const VBucket &localData, + const LogInfo &cloudInfo, VBucket &cloudData) const override; + + bool JudgeUpdateCursor() const override; + + bool JudgeUpload() const override; + + bool JudgeLocker() const override; + + bool JudgeQueryLocalData() const override; + + void SetCloudConflictHandler(const std::weak_ptr &handler) override; +private: + static OpType ConvertToOpType(bool isLocalExist, ConflictRet conflictRet); + mutable std::mutex handlerMutex_; + std::weak_ptr conflictHandler_; +}; +} // DistributedDB +#endif // CLOUD_CUSTOM_PULL_STRATEGY_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc25aad9c66a185560178585912754122827d3da --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_custom_push_strategy.h" + +namespace DistributedDB { +bool CloudCustomPushStrategy::JudgeUpdateCursor() const +{ + return false; +} + +bool CloudCustomPushStrategy::JudgeUpload() const +{ + return true; +} + +bool CloudCustomPushStrategy::JudgeDownload() const +{ + return false; +} + +bool CloudCustomPushStrategy::JudgeLocker() const +{ + return false; +} +} // DistributedDB \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.h new file mode 100644 index 0000000000000000000000000000000000000000..8cdd0b910edd0f1beb84e181ac5af37d8a69fe1e --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_custom_push_strategy.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_CUSTOM_PUSH_STRATEGY_H +#define CLOUD_CUSTOM_PUSH_STRATEGY_H + +#include "cloud_sync_strategy.h" + +namespace DistributedDB { +class CloudCustomPushStrategy : public CloudSyncStrategy { +public: + bool JudgeUpdateCursor() const override; + + bool JudgeUpload() const override; + + bool JudgeDownload() const override; + + bool JudgeLocker() const override; +}; +} // DistributedDB +#endif // CLOUD_CUSTOM_PUSH_STRATEGY_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.cpp similarity index 90% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.cpp rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.cpp index d5a790448b96a967bf10a38d1a3ca8070e60f0fa..c292f53bcdf546bbda6f79d4fc5e95a37a85810a 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.cpp @@ -16,9 +16,8 @@ #include "cloud/cloud_storage_utils.h" namespace DistributedDB { - OpType CloudForcePullStrategy::TagSyncDataStatus(bool existInLocal, [[gnu::unused]] bool isCloudWin, - const LogInfo &localInfo, const LogInfo &cloudInfo) + const LogInfo &localInfo, const LogInfo &cloudInfo) const { if (CloudStorageUtils::IsDataLocked(localInfo.status)) { return OpType::LOCKED_NOT_HANDLE; @@ -47,12 +46,12 @@ OpType CloudForcePullStrategy::TagSyncDataStatus(bool existInLocal, [[gnu::unuse return TagUpdateLocal(cloudInfo, localInfo); } -bool CloudForcePullStrategy::JudgeUpdateCursor() +bool CloudForcePullStrategy::JudgeUpdateCursor() const { return true; } -bool CloudForcePullStrategy::JudgeUpload() +bool CloudForcePullStrategy::JudgeUpload() const { return false; } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.h similarity index 87% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.h rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.h index b090225825a5a42b6c4d265387c7b360c574d5a5..37226ded97e0b9d4702326467ae006971581ad2b 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_pull_strategy.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_pull_strategy.h @@ -20,11 +20,11 @@ namespace DistributedDB { class CloudForcePullStrategy : public CloudSyncStrategy { public: OpType TagSyncDataStatus(bool existInLocal, bool isCloudWin, const LogInfo &localInfo, - const LogInfo &cloudInfo) override; + const LogInfo &cloudInfo) const override; - bool JudgeUpdateCursor() override; + bool JudgeUpdateCursor() const override; - bool JudgeUpload() override; + bool JudgeUpload() const override; }; } #endif // CLOUD_FORCE_PULL_STRATEGY_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.cpp similarity index 91% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.cpp rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.cpp index 9b108f67ffab3a4f1b9355396b789080cedb1cef..9d0e2190ee81cd379b07f2d00d14dc131ed4ac78 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.cpp @@ -18,7 +18,7 @@ namespace DistributedDB { const std::string cloud_device_name = "cloud"; OpType CloudForcePushStrategy::TagSyncDataStatus(bool existInLocal, [[gnu::unused]] bool isCloudWin, - const LogInfo &localInfo, const LogInfo &cloudInfo) + const LogInfo &localInfo, const LogInfo &cloudInfo) const { if (CloudStorageUtils::IsDataLocked(localInfo.status)) { return OpType::LOCKED_NOT_HANDLE; @@ -50,12 +50,12 @@ OpType CloudForcePushStrategy::TagSyncDataStatus(bool existInLocal, [[gnu::unuse return OpType::NOT_HANDLE; } -bool CloudForcePushStrategy::JudgeUpdateCursor() +bool CloudForcePushStrategy::JudgeUpdateCursor() const { return false; } -bool CloudForcePushStrategy::JudgeUpload() +bool CloudForcePushStrategy::JudgeUpload() const { return true; } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.h similarity index 87% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.h rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.h index 184155e66307796cf989f0c0d1d7fa905c1737a0..0fab21028d2be8bedccd6c913366f109ef9dba39 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_force_push_strategy.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_force_push_strategy.h @@ -20,11 +20,11 @@ namespace DistributedDB { class CloudForcePushStrategy : public CloudSyncStrategy { public: OpType TagSyncDataStatus(bool existInLocal, bool isCloudWin, const LogInfo &localInfo, - const LogInfo &cloudInfo) override; + const LogInfo &cloudInfo) const override; - bool JudgeUpdateCursor() override; + bool JudgeUpdateCursor() const override; - bool JudgeUpload() override; + bool JudgeUpload() const override; }; } #endif // CLOUD_FORCE_PUSH_STRATEGY_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.cpp similarity index 96% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.cpp rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.cpp index aca332e10b1d4dfc5e7b0be73ddb0c805ef1f3d1..489a0f25251b5ec646e2ee3b6c3495974765d03f 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.cpp @@ -12,13 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "cloud/cloud_merge_strategy.h" +#include "cloud_merge_strategy.h" #include "cloud/cloud_storage_utils.h" namespace DistributedDB { - OpType CloudMergeStrategy::TagSyncDataStatus(bool existInLocal, bool isCloudWin, const LogInfo &localInfo, - const LogInfo &cloudInfo) + const LogInfo &cloudInfo) const { bool isCloudDelete = IsDelete(cloudInfo); bool isLocalDelete = IsDelete(localInfo); @@ -55,12 +54,12 @@ OpType CloudMergeStrategy::TagSyncDataStatus(bool existInLocal, bool isCloudWin, return TagLoginUserAndUpdate(localInfo, cloudInfo); } -bool CloudMergeStrategy::JudgeUpdateCursor() +bool CloudMergeStrategy::JudgeUpdateCursor() const { return true; } -bool CloudMergeStrategy::JudgeUpload() +bool CloudMergeStrategy::JudgeUpload() const { return true; } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.h similarity index 87% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.h rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.h index cdb9d4a84a663a40e83a02252c5f953b7fb82be3..4d8a5d6c6a31ecbe340e2ce73dba1c40c15e6a82 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_merge_strategy.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_merge_strategy.h @@ -24,14 +24,14 @@ public: ~CloudMergeStrategy() override = default; OpType TagSyncDataStatus(bool existInLocal, bool isCloudWin, const LogInfo &localInfo, - const LogInfo &cloudInfo) override; + const LogInfo &cloudInfo) const override; - bool JudgeUpdateCursor() override; + bool JudgeUpdateCursor() const override; - bool JudgeUpload() override; + bool JudgeUpload() const override; private: - OpType TagLocallyNewer(const LogInfo &localInfo, const LogInfo &cloudInfo, bool isCloudDelete, bool isLocalDelete) - const; + OpType TagLocallyNewer(const LogInfo &localInfo, const LogInfo &cloudInfo, bool isCloudDelete, + bool isLocalDelete) const; OpType TagCloudUpdateLocal(const LogInfo &localInfo, const LogInfo &cloudInfo, bool isCloudDelete, bool isLocalDelete) const; diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.cpp similarity index 77% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.cpp rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.cpp index c4fd79171d78c7375bf3e50ab63b29bd7a7213e4..3f016179f605e2740db83c21da2b659af1cebcc3 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.cpp @@ -30,27 +30,58 @@ void CloudSyncStrategy::SetConflictResolvePolicy(SingleVerConflictResolvePolicy policy_ = policy; } +SingleVerConflictResolvePolicy CloudSyncStrategy::GetConflictResolvePolicy() const +{ + return policy_; +} + +OpType CloudSyncStrategy::TagSyncDataStatus(const DataStatusInfo &statusInfo, + const LogInfo &localInfo, [[gnu::unused]] const VBucket &localData, + const LogInfo &cloudInfo, [[gnu::unused]] VBucket &cloudData) const +{ + return TagSyncDataStatus(statusInfo.isExistInLocal, statusInfo.isCloudWin, localInfo, cloudInfo); +} + OpType CloudSyncStrategy::TagSyncDataStatus([[gnu::unused]] bool existInLocal, [[gnu::unused]] bool isCloudWin, - [[gnu::unused]] const LogInfo &localInfo, [[gnu::unused]] const LogInfo &cloudInfo) + [[gnu::unused]] const LogInfo &localInfo, [[gnu::unused]] const LogInfo &cloudInfo) const { return OpType::NOT_HANDLE; } -bool CloudSyncStrategy::JudgeUpdateCursor() +bool CloudSyncStrategy::JudgeUpdateCursor() const { return false; } -bool CloudSyncStrategy::JudgeUpload() +bool CloudSyncStrategy::JudgeUpload() const { return false; } +bool CloudSyncStrategy::JudgeDownload() const +{ + return true; +} + +bool CloudSyncStrategy::JudgeLocker() const +{ + return true; +} + bool CloudSyncStrategy::JudgeKvScene() const { return isKvScene_; } +bool CloudSyncStrategy::JudgeQueryLocalData() const +{ + return false; +} + +void CloudSyncStrategy::SetCloudConflictHandler([[gnu::unused]] const std::weak_ptr &handler) +{ +} + bool CloudSyncStrategy::IsDelete(const LogInfo &info) { return (info.flag & static_cast(LogInfoFlag::FLAG_DELETE)) == @@ -62,14 +93,6 @@ bool CloudSyncStrategy::IsLogNeedUpdate(const LogInfo &cloudInfo, const LogInfo return (cloudInfo.sharingResource != localInfo.sharingResource) || (cloudInfo.version != localInfo.version); } -bool CloudSyncStrategy::IsSameVersion(const LogInfo &cloudInfo, const LogInfo &localInfo) -{ - if (cloudInfo.version.empty() || localInfo.version.empty()) { - return false; - } - return (cloudInfo.version == localInfo.version); -} - bool CloudSyncStrategy::IsIgnoreUpdate(const LogInfo &localInfo) const { if (policy_ == SingleVerConflictResolvePolicy::DEFAULT_LAST_WIN) { diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.h similarity index 71% rename from frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.h rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.h index 10db3373bc76003b2f9b2955a3882bd79c069102..70839d6e4ee6e025d79a114319a1602ff44bd2df 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_sync_strategy.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/cloud_sync_strategy.h @@ -16,12 +16,18 @@ #ifndef CLOUD_SYNC_STRATEGY_H #define CLOUD_SYNC_STRATEGY_H +#include "cloud/icloud_conflict_handler.h" #include "data_transformer.h" #include "db_errno.h" #include "db_types.h" #include "icloud_sync_storage_interface.h" namespace DistributedDB { +struct DataStatusInfo { + bool isExistInLocal = false; + bool isCloudWin = false; + std::string table; +}; class CloudSyncStrategy { public: CloudSyncStrategy(); @@ -31,23 +37,35 @@ public: void SetConflictResolvePolicy(SingleVerConflictResolvePolicy policy); + SingleVerConflictResolvePolicy GetConflictResolvePolicy() const; + + virtual OpType TagSyncDataStatus(const DataStatusInfo &statusInfo, + const LogInfo &localInfo, const VBucket &localData, + const LogInfo &cloudInfo, VBucket &cloudData) const; + virtual OpType TagSyncDataStatus(bool existInLocal, bool isCloudWin, const LogInfo &localInfo, - const LogInfo &cloudInfo); + const LogInfo &cloudInfo) const; + + virtual bool JudgeUpdateCursor() const; - virtual bool JudgeUpdateCursor(); + virtual bool JudgeUpload() const; - virtual bool JudgeUpload(); + virtual bool JudgeDownload() const; + + virtual bool JudgeLocker() const; bool JudgeKvScene() const; + virtual bool JudgeQueryLocalData() const; + + virtual void SetCloudConflictHandler(const std::weak_ptr &handler); + static bool IsDelete(const LogInfo &info); static bool IsLogNeedUpdate(const LogInfo &cloudInfo, const LogInfo &localInfo); OpType TagUpdateLocal(const LogInfo &cloudInfo, const LogInfo &localInfo) const; protected: - static bool IsSameVersion(const LogInfo &cloudInfo, const LogInfo &localInfo); - bool IsIgnoreUpdate(const LogInfo &localInfo) const; static bool IsSameRecord(const LogInfo &cloudInfo, const LogInfo &localInfo); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy_factory.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/strategy_factory.cpp similarity index 76% rename from frameworks/libs/distributeddb/syncer/src/cloud/strategy_factory.cpp rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/strategy_factory.cpp index f8fb527208db33cd60bdd86463837bb039406891..3e5314b57bc82d7f87fc9aae94a821989ee89ed8 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/strategy_factory.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/strategy_factory.cpp @@ -13,9 +13,11 @@ * limitations under the License. */ -#include "cloud/cloud_force_pull_strategy.h" -#include "cloud/cloud_force_push_strategy.h" -#include "cloud/cloud_merge_strategy.h" +#include "cloud_custom_push_strategy.h" +#include "cloud_custom_pull_strategy.h" +#include "cloud_force_pull_strategy.h" +#include "cloud_force_push_strategy.h" +#include "cloud_merge_strategy.h" #include "strategy_factory.h" namespace DistributedDB { @@ -33,6 +35,12 @@ std::shared_ptr StrategyFactory::BuildSyncStrategy( case SyncMode::SYNC_MODE_CLOUD_FORCE_PUSH: strategy = std::make_shared(); break; + case SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL: + strategy = std::make_shared(); + break; + case SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH: + strategy = std::make_shared(); + break; default: LOGW("[StrategyFactory] Not support mode %d", static_cast(mode)); strategy = std::make_shared(); diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy_factory.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy/strategy_factory.h similarity index 100% rename from frameworks/libs/distributeddb/syncer/src/cloud/strategy_factory.h rename to frameworks/libs/distributeddb/syncer/src/cloud/strategy/strategy_factory.h diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd8fafa81c9f5e966160923c921700ad93f0faa6 --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "cloud/strategy_proxy.h" + +#include "cloud/cloud_sync_utils.h" +#include "cloud/strategy/strategy_factory.h" +namespace DistributedDB { +StrategyProxy::StrategyProxy(const StrategyProxy &other) +{ + CopyStrategy(other); +} + +StrategyProxy &StrategyProxy::operator=(const StrategyProxy &other) +{ + if (&other == this) { + return *this; + } + CopyStrategy(other); + return *this; +} + +void StrategyProxy::CopyStrategy(const StrategyProxy &other) +{ + std::scoped_lock scopedLock(strategyMutex_, other.strategyMutex_); + strategy_ = other.strategy_; +} + +void StrategyProxy::UpdateStrategy(SyncMode mode, bool isKvScene, SingleVerConflictResolvePolicy policy, + const std::weak_ptr &handler) +{ + auto strategy = StrategyFactory::BuildSyncStrategy(mode, isKvScene, policy); + std::lock_guard autoLock(strategyMutex_); + strategy_ = strategy; + strategy_->SetCloudConflictHandler(handler); +} + +void StrategyProxy::ResetStrategy() +{ + std::lock_guard autoLock(strategyMutex_); + strategy_ = nullptr; +} + +void StrategyProxy::SetCloudConflictHandler(const std::shared_ptr &handler) +{ + std::lock_guard autoLock(strategyMutex_); + handler_ = handler; +} + +bool StrategyProxy::JudgeUpload() const +{ + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Judge upload without strategy"); + return false; + } + return strategy_->JudgeUpload(); +} + +bool StrategyProxy::JudgeUpdateCursor() const +{ + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Judge update cursor without strategy"); + return false; + } + return strategy_->JudgeUpdateCursor(); +} + +bool StrategyProxy::JudgeDownload() const +{ + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Judge download without strategy"); + return false; + } + return strategy_->JudgeDownload(); +} + +bool StrategyProxy::JudgeLocker() const +{ + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Judge locker without strategy"); + return false; + } + return strategy_->JudgeLocker(); +} + +bool StrategyProxy::JudgeQueryLocalData() const +{ + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Judge query local without strategy"); + return false; + } + return strategy_->JudgeQueryLocalData(); +} + +std::pair StrategyProxy::TagStatus(bool isExist, size_t idx, const std::shared_ptr &storage, + ICloudSyncer::SyncParam ¶m, ICloudSyncer::DataInfo &dataInfo) const +{ + // Get cloudLogInfo from cloud data + dataInfo.cloudLogInfo = CloudSyncUtils::GetCloudLogInfo(param.downloadData.data[idx]); + auto res = TagStatusByStrategy(isExist, idx, storage, param, dataInfo); + param.downloadData.opType[idx] = res.second; + return res; +} + +std::pair StrategyProxy::TagStatusByStrategy(bool isExist, size_t idx, + const std::shared_ptr &storage, + ICloudSyncer::SyncParam ¶m, ICloudSyncer::DataInfo &dataInfo) const +{ + std::pair res = {E_OK, OpType::NOT_HANDLE}; + auto &[errCode, strategyOpResult] = res; + // ignore same record with local generate data + if (dataInfo.localInfo.logInfo.device.empty() && + !CloudSyncUtils::NeedSaveData(dataInfo.localInfo.logInfo, dataInfo.cloudLogInfo)) { + // not handle same data + return res; + } + std::shared_ptr strategy; + { + std::lock_guard autoLock(strategyMutex_); + if (strategy_ == nullptr) { + LOGW("[StrategyProxy] Tag data status without strategy"); + errCode = -E_INTERNAL_ERROR; + return res; + } + strategy = strategy_; + } + bool isCloudWin = storage->IsTagCloudUpdateLocal(dataInfo.localInfo.logInfo, + dataInfo.cloudLogInfo, strategy->GetConflictResolvePolicy()); + strategyOpResult = strategy->TagSyncDataStatus({isExist, isCloudWin, param.tableName}, + dataInfo.localInfo.logInfo, dataInfo.localInfo.localData, dataInfo.cloudLogInfo, param.downloadData.data[idx]); + if (strategyOpResult == OpType::DELETE) { + param.deletePrimaryKeySet.insert(dataInfo.localInfo.logInfo.hashKey); + } + return res; +} +} \ No newline at end of file diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.h b/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..db31c3dcbfdd3ab900cd91b50cf6cbba61d6e2b1 --- /dev/null +++ b/frameworks/libs/distributeddb/syncer/src/cloud/strategy_proxy.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONFLICT_HANDLER_H +#define CONFLICT_HANDLER_H + +#include "cloud/icloud_conflict_handler.h" +#include "cloud/icloud_syncer.h" +#include "cloud/strategy/cloud_sync_strategy.h" +#include "storage_proxy.h" + +namespace DistributedDB { +class StrategyProxy { +public: + StrategyProxy() = default; + ~StrategyProxy() = default; + + StrategyProxy(const StrategyProxy &other); + StrategyProxy &operator=(const StrategyProxy &other); + + bool JudgeUpload() const; + bool JudgeUpdateCursor() const; + bool JudgeDownload() const; + bool JudgeLocker() const; + bool JudgeQueryLocalData() const; + std::pair TagStatus(bool isExist, size_t idx, const std::shared_ptr &storage, + ICloudSyncer::SyncParam ¶m, ICloudSyncer::DataInfo &dataInfo) const; + std::pair TagStatusByStrategy(bool isExist, size_t idx, const std::shared_ptr &storage, + ICloudSyncer::SyncParam ¶m, ICloudSyncer::DataInfo &dataInfo) const; + void UpdateStrategy(SyncMode mode, bool isKvScene, SingleVerConflictResolvePolicy policy, + const std::weak_ptr &handler); + void ResetStrategy(); + void SetCloudConflictHandler(const std::shared_ptr &handler); +private: + void CopyStrategy(const StrategyProxy &other); + mutable std::mutex strategyMutex_; + std::shared_ptr strategy_; + std::shared_ptr handler_; +}; +} +#endif // CONFLICT_HANDLER_H diff --git a/frameworks/libs/distributeddb/test/BUILD.gn b/frameworks/libs/distributeddb/test/BUILD.gn index 7875721337cccd4d984da4ba3464970acd6edbc6..04111eb8335725aacdad5bec7891d9e965ec3621 100644 --- a/frameworks/libs/distributeddb/test/BUILD.gn +++ b/frameworks/libs/distributeddb/test/BUILD.gn @@ -917,12 +917,22 @@ distributeddb_unittest("DistributedDBKvPermissionSyncTest") { ] } +distributeddb_unittest("DistributedDBInterfacesStopTaskTest") { + sources = [ + "unittest/common/interfaces/distributeddb_interfaces_stop_task_test.cpp", + ] +} + distributeddb_unittest("DistributedDBRDBPermissionSyncTest") { sources = [ "unittest/common/store_test/rdb/distributeddb_rdb_permission_sync_test.cpp", ] } +distributeddb_unittest("DistributedDBRDBConflictHandlerTest") { + sources = [ "unittest/common/store_test/rdb/distributeddb_rdb_conflict_handler_test.cpp" ] +} + distributeddb_unittest("DistributedDBRDBComplexCloudTest") { sources = [ "unittest/common/store_test/rdb/distributeddb_rdb_complex_cloud_test.cpp", @@ -1077,6 +1087,7 @@ group("unittest") { ":DistributedDBInterfacesSchemaDatabaseUpgradeTest", ":DistributedDBInterfacesSingleVersionResultSetTest", ":DistributedDBInterfacesSpaceManagementTest", + ":DistributedDBInterfacesStopTaskTest", ":DistributedDBInterfacesTransactionOptimizationTest", ":DistributedDBJsonPrecheckUnitTest", ":DistributedDBKVCompressTest", @@ -1095,6 +1106,7 @@ group("unittest") { ":DistributedDBRDBCollaborationTest", ":DistributedDBRDBComplexCloudTest", ":DistributedDBRDBCompressTest", + ":DistributedDBRDBConflictHandlerTest", ":DistributedDBRDBDataStatusTest", ":DistributedDBRDBDropTableTest", ":DistributedDBRDBKnowledgeClientTest", diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp index 2589b519950066401cf3cd6ef3298a97bbd34b23..3f22df1c842583f7ea9e9bc59925e0b6e73fb285 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp @@ -642,4 +642,13 @@ int RDBGeneralUt::PutMetaData(const StoreInfo &store, const Key &key, const Valu } return SQLiteRelationalUtils::PutKvData(db, false, key, value); } + +void RDBGeneralUt::SetCloudConflictHandler(const StoreInfo &store, const ConflictCallback &callback) +{ + auto delegate = GetDelegate(store); + ASSERT_NE(delegate, nullptr); + auto handler = std::make_shared(); + handler->SetCallback(callback); + EXPECT_EQ(delegate->SetCloudConflictHandler(handler), OK); +} } \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h index a17a3c694bed6b71d4af8bec081aed8cdfaf5ac0..18da1f4064e2b2560328237c6018991120a4e15e 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h @@ -26,6 +26,25 @@ const std::string g_defaultTable1 = "defaultTable1"; const std::string g_defaultTable2 = "defaultTable2"; const std::vector PASSWD_VECTOR = {'P', 'a', 's', 's', 'w', 'o', 'r', 'd', '@', '1'}; +using ConflictCallback = std::function; +class TestCloudConflictHandler : public ICloudConflictHandler { +public: + ConflictRet HandleConflict(const std::string &table, const VBucket &oldData, const VBucket &newData, + VBucket &upsert) override + { + if (callback_) { + return callback_(table, oldData, newData, upsert); + } + return ConflictRet::NOT_HANDLE; + } + + void SetCallback(const ConflictCallback &callback) + { + callback_ = callback; + } +protected: + ConflictCallback callback_; +}; class RDBGeneralUt : public BasicUnitTest { public: void SetUp() override; @@ -105,6 +124,8 @@ protected: int PutMetaData(const StoreInfo &store, const Key &key, const Value &value); + void SetCloudConflictHandler(const StoreInfo &store, const ConflictCallback &callback); + mutable std::mutex storeMutex_; std::map stores_; std::map sqliteDb_; diff --git a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_set_cloud_schema_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_set_cloud_schema_test.cpp index 2f022443da1adb7af874c95ada1ca1cbabffbfff..875cd204b8d4a9bea7f54197b5b4f8061a6ba0ea 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_set_cloud_schema_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_set_cloud_schema_test.cpp @@ -1226,8 +1226,8 @@ namespace { */ DataBaseSchema dataBaseSchema; TableSchema tableSchema = { - .name = g_tableName1, - .sharedTableName = g_sharedTableName1, + .name = g_tableName2, + .sharedTableName = g_sharedTableName2, .fields = g_cloudField1 }; dataBaseSchema.tables.push_back(tableSchema); @@ -1237,11 +1237,11 @@ namespace { * @tc.steps:step2. insert local shared table records and sync * @tc.expected: step2. return OK */ - InsertLocalSharedTableRecords(0, 10, g_sharedTableName1); + InsertLocalSharedTableRecords(0, 10, g_sharedTableName2); g_virtualCloudDb->ForkUpload([](const std::string &tableName, VBucket &extend) { extend.erase(CloudDbConstant::VERSION_FIELD); }); - Query query = Query::Select().FromTable({ g_sharedTableName1 }); + Query query = Query::Select().FromTable({ g_sharedTableName2 }); BlockSync(query, g_delegate, DBStatus::OK); g_virtualCloudDb->ForkUpload([](const std::string &tableName, VBucket &extend) { @@ -1249,7 +1249,7 @@ namespace { extend[CloudDbConstant::VERSION_FIELD] = ""; } }); - query = Query::Select().FromTable({ g_sharedTableName1 }); + query = Query::Select().FromTable({ g_sharedTableName2 }); BlockSync(query, g_delegate, DBStatus::OK); } @@ -1268,14 +1268,14 @@ namespace { */ DataBaseSchema dataBaseSchema; TableSchema tableSchema = { - .name = g_tableName1, - .sharedTableName = g_sharedTableName1, + .name = g_tableName2, + .sharedTableName = g_sharedTableName2, .fields = g_cloudField1 }; dataBaseSchema.tables.push_back(tableSchema); ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); - InsertLocalSharedTableRecords(0, 10, g_sharedTableName1); // 10 is records num - Query query = Query::Select().FromTable({ g_sharedTableName1 }); + InsertLocalSharedTableRecords(0, 10, g_sharedTableName2); // 10 is records num + Query query = Query::Select().FromTable({ g_sharedTableName2 }); BlockSync(query, g_delegate); /** @@ -1288,7 +1288,7 @@ namespace { * @tc.steps:step3. check if the hash of assets in db is empty * @tc.expected: step3. OK */ - std::string sql = "SELECT asset from " + g_sharedTableName1; + std::string sql = "SELECT asset from " + g_sharedTableName2; sqlite3_stmt *stmt = nullptr; ASSERT_EQ(SQLiteUtils::GetStatement(db_, sql, stmt), E_OK); while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { @@ -1320,22 +1320,22 @@ namespace { */ DataBaseSchema dataBaseSchema; TableSchema tableSchema = { - .name = g_tableName1, - .sharedTableName = g_sharedTableName1, + .name = g_tableName2, + .sharedTableName = g_sharedTableName2, .fields = g_cloudField1 }; dataBaseSchema.tables.push_back(tableSchema); ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); - CheckSharedTable({g_sharedTableName1}); + CheckSharedTable({g_sharedTableName2}); /** * @tc.steps:step2. insert local shared table records and alter shared table name then sync * @tc.expected: step2. return OK */ - InsertLocalSharedTableRecords(0, 10, g_sharedTableName1); + InsertLocalSharedTableRecords(0, 10, g_sharedTableName2); dataBaseSchema.tables.clear(); tableSchema = { - .name = g_tableName1, + .name = g_tableName2, .sharedTableName = g_sharedTableName5, .fields = g_cloudField1 }; @@ -1362,18 +1362,18 @@ namespace { */ DataBaseSchema dataBaseSchema; TableSchema tableSchema = { - .name = g_tableName1, - .sharedTableName = g_sharedTableName1, + .name = g_tableName2, + .sharedTableName = g_sharedTableName2, .fields = g_cloudField1 }; dataBaseSchema.tables.push_back(tableSchema); ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); int localCount = 10; - InsertLocalSharedTableRecords(0, localCount, g_sharedTableName1); - Query query = Query::Select().FromTable({ g_sharedTableName1 }); + ASSERT_NO_FATAL_FAILURE(InsertLocalSharedTableRecords(0, localCount, g_sharedTableName2)); + Query query = Query::Select().FromTable({ g_sharedTableName2 }); BlockSync(query, g_delegate, DBStatus::OK); - CheckCloudTableCount(g_sharedTableName1, localCount); + CheckCloudTableCount(g_sharedTableName2, localCount); /** * @tc.steps:step2. add shared table column and sync @@ -1381,16 +1381,16 @@ namespace { */ dataBaseSchema.tables.clear(); tableSchema = { - .name = g_tableName1, - .sharedTableName = g_sharedTableName1, + .name = g_tableName2, + .sharedTableName = g_sharedTableName2, .fields = g_cloudField3 }; dataBaseSchema.tables.push_back(tableSchema); ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); int cloudCount = 20; - InsertLocalSharedTableRecords(localCount, cloudCount, g_sharedTableName1, true); + ASSERT_NO_FATAL_FAILURE(InsertLocalSharedTableRecords(localCount, cloudCount, g_sharedTableName2)); BlockSync(query, g_delegate, DBStatus::OK); - CheckCloudTableCount(g_sharedTableName1, cloudCount); + CheckCloudTableCount(g_sharedTableName2, cloudCount); } /** diff --git a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_stop_task_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_stop_task_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6868a9bca403926bda848f360ca3f28f937ba28d --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_interfaces_stop_task_test.cpp @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef USE_DISTRIBUTEDDB_CLOUD +#include +#include + +#include "db_common.h" +#include "distributeddb_data_generate_unit_test.h" +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" +#include "relational_store_manager.h" +#include "runtime_config.h" +#include "sqlite_meta_executor.h" +#include "virtual_cloud_db.h" +#include "virtual_communicator_aggregator.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +const int ONE_BATCH_NUM = 1000; +constexpr std::chrono::milliseconds SYNC_MAX_TIME = std::chrono::milliseconds(10 * 60 * 1000); +constexpr const char* DB_SUFFIX = ".db"; +constexpr const char* STORE_ID = "RelationalAsyncCreateDistributedDBTableTest"; +constexpr const char *ASYNC_GEN_LOG_TASK_PREFIX = "async_generate_log_task_"; +const std::string NORMAL_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS test_table(" \ + "id INT PRIMARY KEY," \ + "name TEXT," \ + "number INT NOT NULL);"; +const std::vector CLOUD_FIELDS = { + {.colName = "id", .type = TYPE_INDEX, .primary = true}, + {.colName = "name", .type = TYPE_INDEX}, + {.colName = "number", .type = TYPE_INDEX, .nullable = false} +}; +std::string g_testDir; +std::string g_dbDir; +const std::string NORMAL_TABLE = "test_table"; +const std::string TEST_TABLE_2 = "test_table_2"; +const std::string NORMAL_SHARED_TABLE = "test_table_shared"; +const std::string META_TABLE = "naturalbase_rdb_aux_metadata"; +sqlite3 *g_db = nullptr; +DistributedDB::RelationalStoreManager g_mgr(APP_ID, USER_ID); +RelationalStoreDelegate *g_delegate = nullptr; +std::shared_ptr g_virtualCloudDb; +VirtualCommunicatorAggregator *g_communicatorAggregator = nullptr; +SyncProcess g_syncProcess; + +class DistributedDBInterfacesStopTaskTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +protected: + static void InsertData(int startId, int dataNum, const std::string &tableName = NORMAL_TABLE); + static void CallSync(DBStatus expectResult = OK); + static void CallSyncWithQuery(const Query &query, DBStatus expectResult = OK); + static int GetDataCount(const std::string &tableName, int &count); + static void WaitForTaskFinished(const std::string &tableName = NORMAL_TABLE); + static void CheckAsyncGenLogFlag(const std::string &tableName, bool &isExist); + static void PrepareTestTable2(bool isCreateDistributedTable = true); + static void PrepareTestTables(); +}; + +void GetCloudDbSchema(DataBaseSchema &dataBaseSchema) +{ + TableSchema normalTableSchema = {.name = NORMAL_TABLE, .sharedTableName = NORMAL_TABLE + "_shared", + .fields = CLOUD_FIELDS}; + dataBaseSchema.tables.push_back(normalTableSchema); +} + +void DistributedDBInterfacesStopTaskTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + g_dbDir = g_testDir + "/"; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); + g_communicatorAggregator = new (std::nothrow) VirtualCommunicatorAggregator(); + ASSERT_TRUE(g_communicatorAggregator != nullptr); + RuntimeContext::GetInstance()->SetCommunicatorAggregator(g_communicatorAggregator); +} + +void DistributedDBInterfacesStopTaskTest::TearDownTestCase(void) +{ + RuntimeContext::GetInstance()->SetCommunicatorAggregator(nullptr); +} + +void DistributedDBInterfacesStopTaskTest::SetUp(void) +{ + DistributedDBToolsUnitTest::PrintTestCaseInfo(); + g_db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(g_db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(g_db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + EXPECT_EQ(RelationalTestUtils::ExecSql(g_db, NORMAL_CREATE_TABLE_SQL), SQLITE_OK); + + DBStatus status = g_mgr.OpenStore(g_dbDir + STORE_ID + DB_SUFFIX, STORE_ID, {}, g_delegate); + EXPECT_EQ(status, OK); + ASSERT_NE(g_delegate, nullptr); + g_virtualCloudDb = make_shared(); + ASSERT_EQ(g_delegate->SetCloudDB(g_virtualCloudDb), DBStatus::OK); + DataBaseSchema dataBaseSchema; + GetCloudDbSchema(dataBaseSchema); + ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); +} + +void DistributedDBInterfacesStopTaskTest::TearDown(void) +{ + if (g_db != nullptr) { + EXPECT_EQ(sqlite3_close_v2(g_db), SQLITE_OK); + g_db = nullptr; + } + if (g_delegate != nullptr) { + DBStatus status = g_mgr.CloseStore(g_delegate); + g_delegate = nullptr; + EXPECT_EQ(status, OK); + } + g_virtualCloudDb = nullptr; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBInterfacesStopTaskTest::InsertData(int startId, int dataNum, const std::string &tableName) +{ + ASSERT_NE(g_db, nullptr); + SQLiteUtils::BeginTransaction(g_db, TransactType::IMMEDIATE); + sqlite3_stmt *stmt = nullptr; + std::string sql = "INSERT INTO " + tableName + "(id, name, number) VALUES(?, ?, ?);"; + EXPECT_EQ(SQLiteUtils::GetStatement(g_db, sql, stmt), E_OK); + int resetRet = E_OK; + for (int i = startId; i < startId + dataNum; i++) { + EXPECT_EQ(SQLiteUtils::BindInt64ToStatement(stmt, 1, i), E_OK); // 1st is id + EXPECT_EQ(SQLiteUtils::BindTextToStatement(stmt, 2, "name_" + std::to_string(i)), E_OK); // 2nd is name + EXPECT_EQ(SQLiteUtils::BindInt64ToStatement(stmt, 3, i + i), E_OK); // 3rd is number + EXPECT_EQ(SQLiteUtils::StepWithRetry(stmt), SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)); + SQLiteUtils::ResetStatement(stmt, false, resetRet); + } + SQLiteUtils::ResetStatement(stmt, true, resetRet); + SQLiteUtils::CommitTransaction(g_db); +} + +void DistributedDBInterfacesStopTaskTest::CallSyncWithQuery(const Query &query, DBStatus expectResult) +{ + CloudSyncOption option; + option.devices = { "CLOUD" }; + option.query = query; + option.mode = SYNC_MODE_CLOUD_MERGE; + std::mutex dataMutex; + std::condition_variable cv; + bool finish = false; + SyncProcess last; + auto callback = [&last, &cv, &dataMutex, &finish](const std::map &process) { + for (const auto &item: process) { + if (item.second.process == DistributedDB::FINISHED) { + { + std::lock_guard autoLock(dataMutex); + finish = true; + last = item.second; + } + cv.notify_one(); + } + } + }; + auto start = std::chrono::steady_clock::now(); + bool isTimeOut = false; + std::thread timeoutCheckThread([&isTimeOut, &dataMutex, &finish, &cv, &start]() { + while (!finish) { + auto duration = std::chrono::duration_cast(std::chrono::steady_clock::now() - + start); + if (duration > SYNC_MAX_TIME) { + isTimeOut = true; + finish = true; + cv.notify_all(); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + }); + ASSERT_EQ(g_delegate->Sync(option, callback), expectResult); + if (expectResult == OK) { + std::unique_lock uniqueLock(dataMutex); + cv.wait(uniqueLock, [&finish]() { + return finish; + }); + } else { + finish = true; + } + timeoutCheckThread.join(); + EXPECT_FALSE(isTimeOut); + g_syncProcess = last; +} + +void DistributedDBInterfacesStopTaskTest::CallSync(DBStatus expectResult) +{ + Query query = Query::Select().FromTable({NORMAL_TABLE}); + CallSyncWithQuery(query, expectResult); +} + +int DistributedDBInterfacesStopTaskTest::GetDataCount(const std::string &tableName, int &count) +{ + std::string sql = "select count(*) from " + tableName; + return SQLiteUtils::GetCountBySql(g_db, sql, count); +} + +void DistributedDBInterfacesStopTaskTest::WaitForTaskFinished(const std::string &tableName) +{ + int loopTimes = 100; + bool isExistTask = true; + while (loopTimes > 0 && isExistTask) { + loopTimes--; + CheckAsyncGenLogFlag(tableName, isExistTask); + std::this_thread::sleep_for(chrono::seconds(1)); // wait 1s to query again. + } +} + +void DistributedDBInterfacesStopTaskTest::CheckAsyncGenLogFlag(const std::string &tableName, bool &isExist) +{ + isExist = false; + Key keyPrefix; + DBCommon::StringToVector(ASYNC_GEN_LOG_TASK_PREFIX, keyPrefix); + std::map asyncTaskMap; + EXPECT_EQ(SqliteMetaExecutor::GetMetaDataByPrefixKey(g_db, false, META_TABLE, keyPrefix, asyncTaskMap), E_OK); + for (const auto &task : asyncTaskMap) { + std::string taskTableName; + DBCommon::VectorToString(task.second, taskTableName); + if (taskTableName == tableName) { + isExist = true; + } + } +} + +void DistributedDBInterfacesStopTaskTest::PrepareTestTable2(bool isCreateDistributedTable) +{ + const std::vector cloudFields = { + {.colName = "id", .type = TYPE_INDEX, .primary = true}, + {.colName = "name", .type = TYPE_INDEX} + }; + TableSchema tableSchema = {.name = TEST_TABLE_2, .sharedTableName = TEST_TABLE_2 + "_shared", + .fields = cloudFields}; + DataBaseSchema dataBaseSchema; + GetCloudDbSchema(dataBaseSchema); + dataBaseSchema.tables.push_back(tableSchema); + ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); + std::string sql = "CREATE TABLE IF NOT EXISTS " + TEST_TABLE_2 + "(id INT PRIMARY KEY, name TEXT);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(g_db, sql), SQLITE_OK); + if (isCreateDistributedTable) { + EXPECT_EQ(g_delegate->CreateDistributedTable(TEST_TABLE_2, CLOUD_COOPERATION), OK); + } +} + +void DistributedDBInterfacesStopTaskTest::PrepareTestTables() +{ + DataBaseSchema dataBaseSchema; + int tableCount = 10; + for (int i = 0; i < tableCount; i++) { + std::string tableName = NORMAL_TABLE + std::to_string(i); + std::string sql = "CREATE TABLE IF NOT EXISTS " + tableName + "(" \ + "id INTEGER PRIMARY KEY AUTOINCREMENT," \ + "name TEXT," \ + "number INT NOT NULL);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(g_db, sql), SQLITE_OK); + for (int j = 0; j < ONE_BATCH_NUM; j++) { + std::string insertSql = "INSERT INTO " + tableName + "(name, number) VALUES('name_" + std::to_string(j) + + "', " + std::to_string(j) + ");"; + SqlCondition condition; + condition.sql = insertSql; + std::vector records; + EXPECT_EQ(g_delegate->ExecuteSql(condition, records), OK); + } + TableSchema tableSchema = {.name = tableName, .sharedTableName = tableName + "_shared", + .fields = CLOUD_FIELDS}; + dataBaseSchema.tables.push_back(tableSchema); + } + ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest001 + * @tc.desc: Test abort create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for task stopped + int logCount = 0; + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_TRUE(logCount > 0); + EXPECT_TRUE(logCount < dataCount); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); + /** + * @tc.steps:step3. Create distributed table again. + * @tc.expected: step3. Return OK. + */ + int newDataCount = 1; + InsertData(dataCount, newDataCount); + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + WaitForTaskFinished(); + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_EQ(logCount, dataCount + newDataCount); + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest002 + * @tc.desc: Test sync after async create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + int logCount = 0; + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_TRUE(logCount > 0); + EXPECT_TRUE(logCount < dataCount); + /** + * @tc.steps:step3. Sync to cloud. + * @tc.expected: step3. Return OK. + */ + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest003 + * @tc.desc: Test update/delete after async create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + int logCount = 0; + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_TRUE(logCount < dataCount); + /** + * @tc.steps:step3. update/delete/insert data and create distributed table again. + * @tc.expected: step3. Return OK. + */ + std::string sql = "delete from " + NORMAL_TABLE + " where id >= 0 and id < 10;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + sql = "update " + NORMAL_TABLE + " set number = number + 1 where id >= 10 and id < 20;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + InsertData(dataCount, ONE_BATCH_NUM); + + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + WaitForTaskFinished(); + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK);; + int actualDeleteCount = 0; + int expectDeleteCount = 10; + EXPECT_TRUE(logCount == dataCount + ONE_BATCH_NUM || logCount == dataCount + ONE_BATCH_NUM - expectDeleteCount); + sql = "select count(*) from " + DBCommon::GetLogTableName(NORMAL_TABLE) + " where data_key = -1"; + EXPECT_EQ(SQLiteUtils::GetCountBySql(g_db, sql, actualDeleteCount), E_OK); + EXPECT_TRUE(actualDeleteCount == expectDeleteCount || actualDeleteCount == 0); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest004 + * @tc.desc: Test update/delete after async create distributed table, and then sync to cloud. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + /** + * @tc.steps:step3. update/delete/insert data sync. + * @tc.expected: step3. Return OK. + */ + int deleteCount = 10; + std::string sql = "delete from " + NORMAL_TABLE + " where id >= 0 and id < 10;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + sql = "update " + NORMAL_TABLE + " set number = number + 1 where id >= 10 and id < 20;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + InsertData(dataCount, ONE_BATCH_NUM); + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount + ONE_BATCH_NUM - deleteCount); + } + + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest005 + * @tc.desc: Test update/delete after abort create distributed table, and then sync to cloud. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + bool isExistFlag = false; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); + /** + * @tc.steps:step3. update/delete/insert data sync. + * @tc.expected: step3. Return OK. + */ + std::string sql = "delete from " + NORMAL_TABLE + " where id >= 0 and id < 10;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + sql = "update " + NORMAL_TABLE + " set number = number + 1 where id >= 10 and id < 20;"; + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(g_db, sql), E_OK); + InsertData(dataCount, ONE_BATCH_NUM); + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + int deleteCount = 10; + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount + ONE_BATCH_NUM - deleteCount); + } + + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest006 + * @tc.desc: Test create distributed table again after abort create distributed table, and then sync to cloud. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + bool isExistFlag = false; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); + /** + * @tc.steps:step3. Create distributed table again and sync. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest007 + * @tc.desc: Test create distributed table again after finish async create distributed table, and then sync to cloud. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest007, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + WaitForTaskFinished(); + /** + * @tc.steps:step3. Create distributed table again and sync. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest008 + * @tc.desc: Test remove device data when async gen log task exist. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and remove device data. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + EXPECT_EQ(g_delegate->RemoveDeviceData("dev", ClearMode::FLAG_AND_DATA), OK); + bool isExistFlag = false; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest009 + * @tc.desc: Test remove device data of table which in async gen log task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest009, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and remove device data. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + ClearDeviceDataOption option = {.mode = ClearMode::FLAG_AND_DATA, .device = "dev", .tableList = {NORMAL_TABLE}}; + EXPECT_EQ(g_delegate->RemoveDeviceData(option), OK); + bool isExistFlag = false; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest010 + * @tc.desc: Test remove device data of table which not in async gen log task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create another table. + * @tc.expected: step2. Return OK. + */ + PrepareTestTable2(); + /** + * @tc.steps:step3. Create distributed table and remove device data. + * @tc.expected: step3. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + ClearDeviceDataOption option = {.mode = ClearMode::FLAG_AND_DATA, .device = "dev", .tableList = {TEST_TABLE_2}}; + EXPECT_EQ(g_delegate->RemoveDeviceData(option), OK); + WaitForTaskFinished(); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest011 + * @tc.desc: Test async create distributed table without data. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest011, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create distributed table without data + * @tc.expected: step1. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); + /** + * @tc.steps:step2. Prepare data and sync to cloud + * @tc.expected: step2. Return OK. + */ + int dataCount = 10; + InsertData(0, dataCount); + + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + + isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest012 + * @tc.desc: Test async create distributed table without data, and then create again after insert data. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest012, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create distributed table without data + * @tc.expected: step1. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); + /** + * @tc.steps:step2. Prepare data and create distributed table again + * @tc.expected: step2. Return OK. + */ + int dataCount = 100; + InsertData(0, dataCount); + /** + * @tc.steps:step3. Sync to cloud. + * @tc.expected: step3. Return OK. + */ + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + + isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest013 + * @tc.desc: Test sync with table which not create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest013, TestSize.Level1) +{ + /** + * @tc.steps:step1. Create distributed table without data + * @tc.expected: step1. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + /** + * @tc.steps:step2. Prepare data and create another table. + * @tc.expected: step2. Return OK. + */ + int dataCount = 100; + InsertData(0, dataCount); + PrepareTestTable2(false); + /** + * @tc.steps:step3. Sync to cloud. + * @tc.expected: step3. Sync failed. + */ + Query query = Query::Select().FromTable({NORMAL_TABLE, TEST_TABLE_2}); + CallSyncWithQuery(query, SCHEMA_MISMATCH); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest014 + * @tc.desc: Test create 2 distributed table, then sync 1 table to cloud + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest014, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data and create another table. + * @tc.expected: step1. Return OK. + */ + PrepareTestTable2(); + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create 2 distributed table without data + * @tc.expected: step2. Return OK. + */ + PrepareTestTable2(); + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + EXPECT_EQ(g_delegate->CreateDistributedTable(TEST_TABLE_2, CLOUD_COOPERATION, {true}), OK); + /** + * @tc.steps:step3. Sync to cloud. + * @tc.expected: step3. Sync succ. + */ + Query query = Query::Select().FromTable({TEST_TABLE_2}); + CallSyncWithQuery(query); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + + CallSync(); + EXPECT_EQ(g_syncProcess.process, FINISHED); + EXPECT_EQ(g_syncProcess.errCode, E_OK); + ASSERT_EQ(g_syncProcess.tableProcess.size(), 1u); + for (const auto &process : g_syncProcess.tableProcess) { + EXPECT_EQ(process.second.process, FINISHED); + EXPECT_EQ(process.second.upLoadInfo.successCount, dataCount); + } + + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest016 + * @tc.desc: Test async create distributed table with type DEVICE_COOPERATION + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest016, TestSize.Level0) +{ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, DEVICE_COOPERATION, {true}), NOT_SUPPORT); +} + +/** +* @tc.name: AsyncCreateDistributedDBTableTest017 +* @tc.desc: Test async create distributed table with multi tables +* @tc.require: +* @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest017, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data and create another table. + * @tc.expected: step1. Return OK. + */ + PrepareTestTable2(); + int dataCount = ONE_BATCH_NUM * 5; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create 2 distributed table without data + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + EXPECT_EQ(g_delegate->CreateDistributedTable(TEST_TABLE_2, CLOUD_COOPERATION, {true}), OK); + /** + * @tc.steps:step3. Wait NORMAL_TABLE finished, check TEST_TABLE_2 + * @tc.expected: step3. Return OK. + */ + WaitForTaskFinished(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool isExistFlag = true; + CheckAsyncGenLogFlag(TEST_TABLE_2, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest018 + * @tc.desc: Test stop async create distributed table and then create distributed table + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest018, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for task stopped + int logCount = 0; + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_TRUE(logCount > 0); + EXPECT_TRUE(logCount < dataCount); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); + /** + * @tc.steps:step3. Create distributed table again. + * @tc.expected: step3. Return OK. + */ + int newDataCount = 1; + InsertData(dataCount, newDataCount); + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION), OK); + EXPECT_EQ(GetDataCount(DBCommon::GetLogTableName(NORMAL_TABLE), logCount), E_OK); + EXPECT_EQ(logCount, dataCount + newDataCount); + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest019 + * @tc.desc: Test async create distributed table when gen log before cloud sync + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest019, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + PrepareTestTable2(); + int dataCount = ONE_BATCH_NUM * 5; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for task stopped + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_TRUE(isExistFlag); + /** + * @tc.steps:step3. Sync and create another distributed table. + * @tc.expected: step3. Return OK. + */ + std::thread syncThread([]() { + CallSync(); + }); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_EQ(g_delegate->CreateDistributedTable(TEST_TABLE_2, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + WaitForTaskFinished(TEST_TABLE_2); + syncThread.join(); +} +/** + * @tc.name: AsyncCreateDistributedDBTableTest020 + * @tc.desc: Test async create distributed table and set tracker table + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest020, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM * 10; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Create distributed table and stop task. + * @tc.expected: step2. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + g_delegate->StopTask(TaskType::BACKGROUND_TASK); + /** + * @tc.steps:step3. Set tracker table. + * @tc.expected: step3. Return NOT_SUPPORT. + */ + TrackerSchema schema; + schema.tableName = NORMAL_TABLE; + schema.extendColNames = {"name"}; + schema.trackerColNames = {"name"}; + EXPECT_EQ(g_delegate->SetTrackerTable(schema), NOT_SUPPORT); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest021 + * @tc.desc: Test async create distributed table and set tracker table + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest021, TestSize.Level1) +{ + /** + * @tc.steps:step1. Set tracker table. + * @tc.expected: step1. Return OK. + */ + TrackerSchema schema; + schema.tableName = NORMAL_TABLE; + schema.extendColNames = {"name"}; + schema.trackerColNames = {"name"}; + EXPECT_EQ(g_delegate->SetTrackerTable(schema), OK); + /** + * @tc.steps:step2. Async create distributed table. + * @tc.expected: step2. Return NOT_SUPPORT. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), NOT_SUPPORT); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest022 + * @tc.desc: Test IsStringAllDigit + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest022, TestSize.Level1) +{ + EXPECT_FALSE(DBCommon::IsStringAllDigit("")); + EXPECT_TRUE(DBCommon::IsStringAllDigit("123")); + EXPECT_FALSE(DBCommon::IsStringAllDigit("a123")); + EXPECT_FALSE(DBCommon::IsStringAllDigit("123a")); + EXPECT_FALSE(DBCommon::IsStringAllDigit("#$%^")); + EXPECT_FALSE(DBCommon::IsStringAllDigit("<>+\n")); +} + +/** + * @tc.name: AsyncCreateDistributedDBTableTest023 + * @tc.desc: Test invalid async gen log task flag + * @tc.require: + * @tc.author: liaoyonghuang +*/ +HWTEST_F(DistributedDBInterfacesStopTaskTest, AsyncCreateDistributedDBTableTest023, TestSize.Level1) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + int dataCount = ONE_BATCH_NUM; + InsertData(0, dataCount); + /** + * @tc.steps:step2. Insert an invalid flag. + * @tc.expected: step2. Return OK. + */ + Key taskKey; + DBCommon::StringToVector(std::string(ASYNC_GEN_LOG_TASK_PREFIX) + "aaa", taskKey); + Value taskVal; + DBCommon::StringToVector("invalid_table", taskVal); + std::string INSERT_META_SQL = "INSERT OR REPLACE INTO " + META_TABLE + " VALUES(?,?);"; + sqlite3_stmt *statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(g_db, INSERT_META_SQL, statement), E_OK); + ASSERT_EQ(SQLiteUtils::BindBlobToStatement(statement, 1, taskKey, false), E_OK); // 1 means key index + ASSERT_EQ(SQLiteUtils::BindBlobToStatement(statement, 2, taskVal, true), E_OK); // 2 means value index + ASSERT_EQ(SQLiteUtils::StepWithRetry(statement, false), SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)); + int errCode = E_OK; + SQLiteUtils::ResetStatement(statement, true, errCode); + /** + * @tc.steps:step3. Insert a flag of no exist table. + * @tc.expected: step3. Return OK. + */ + DBCommon::StringToVector(std::string(ASYNC_GEN_LOG_TASK_PREFIX) + "2", taskKey); + DBCommon::StringToVector("invalid_table", taskVal); + statement = nullptr; + ASSERT_EQ(SQLiteUtils::GetStatement(g_db, INSERT_META_SQL, statement), E_OK); + ASSERT_EQ(SQLiteUtils::BindBlobToStatement(statement, 1, taskKey, false), E_OK); // 1 means key index + ASSERT_EQ(SQLiteUtils::BindBlobToStatement(statement, 2, taskVal, true), E_OK); // 2 means value index + ASSERT_EQ(SQLiteUtils::StepWithRetry(statement, false), SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)); + SQLiteUtils::ResetStatement(statement, true, errCode); + /** + * @tc.steps:step4. Create distributed table and stop task. + * @tc.expected: step4. Return OK. + */ + EXPECT_EQ(g_delegate->CreateDistributedTable(NORMAL_TABLE, CLOUD_COOPERATION, {true}), OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + WaitForTaskFinished(); + bool isExistFlag = true; + CheckAsyncGenLogFlag(NORMAL_TABLE, isExistFlag); + EXPECT_FALSE(isExistFlag); +} +#endif diff --git a/frameworks/libs/distributeddb/test/unittest/common/storage/cloud/distributeddb_cloud_check_sync_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/storage/cloud/distributeddb_cloud_check_sync_test.cpp index 351f4f2f9399c0cc769818e77296de373544ce4f..b2f265878fd5f9e96466ed14c1cfca3f612995e4 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/storage/cloud/distributeddb_cloud_check_sync_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/storage/cloud/distributeddb_cloud_check_sync_test.cpp @@ -724,7 +724,6 @@ HWTEST_F(DistributedDBCloudCheckSyncTest, CloudSyncTest003, TestSize.Level1) // prepare data const int actualCount = 1; InsertUserTableRecord(tableName_, actualCount); - InsertCloudTableRecord(0, actualCount, 0, false); // delete local data DeleteUserTableRecord(0); @@ -1138,7 +1137,7 @@ HWTEST_F(DistributedDBCloudCheckSyncTest, CloudSyncTest010, TestSize.Level1) ASSERT_EQ(delegate_->SetCloudDB(virtualCloudDb_), DBStatus::OK); ASSERT_EQ(delegate_->SetIAssetLoader(virtualAssetLoader_), DBStatus::OK); DataBaseSchema dataBaseSchema = GetSchema(); - ASSERT_EQ(delegate_->SetCloudDbSchema(dataBaseSchema), DBStatus::OK); + ASSERT_EQ(delegate_->SetCloudDbSchema(dataBaseSchema), DBStatus::INVALID_ARGS); communicatorAggregator_ = new (std::nothrow) VirtualCommunicatorAggregator(); ASSERT_TRUE(communicatorAggregator_ != nullptr); RuntimeContext::GetInstance()->SetCommunicatorAggregator(communicatorAggregator_); diff --git a/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp index 549f5102516af11dcddd12c179e2e79449451af9..a2adbebde65bbb391aa268bc9407047c470fc690 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_query_object_helper_test.cpp @@ -223,7 +223,7 @@ std::vector> Query004GetExpectNode() .fieldName = "", .fieldValue = {} }, QueryNode { - .type = QueryNodeType::EQUAL_TO, + .type = QueryNodeType::NOT_EQUAL_TO, .fieldName = "field2", .fieldValue = {std::string("2")} }, QueryNode { @@ -280,7 +280,7 @@ HWTEST_F(DistributedDBQueryObjectHelperTest, Query004, TestSize.Level1) { std::vector inVal = {"1", "2", "3"}; Query query = Query::Select().From("table1").BeginGroup(). - EqualTo("field1", "1").And().EqualTo("field2", "2"). + EqualTo("field1", "1").And().NotEqualTo("field2", "2"). EndGroup().Or().BeginGroup().EqualTo("field1", "2").And().EqualTo("field2", "1").EndGroup(). From("table2").In("field3", inVal); diff --git a/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_relational_syncable_storage_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_relational_syncable_storage_test.cpp index 982a223d1025230e0097d4a79a916db7fb25725d..e86d1577b64167be879af9ecc7c9db2da68ac44f 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_relational_syncable_storage_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/storage/distributeddb_relational_syncable_storage_test.cpp @@ -136,7 +136,7 @@ HWTEST_F(DistributedDBRelationalSyncableStorageTest, FuncExceptionTest001, TestS auto conn = std::make_shared(nullptr); EXPECT_EQ(conn->Close(), -E_INVALID_CONNECTION); EXPECT_EQ(conn->GetIdentifier().size(), 0u); - EXPECT_EQ(conn->CreateDistributedTable("", {}), -E_INVALID_CONNECTION); + EXPECT_EQ(conn->CreateDistributedTable("", {}, false), -E_INVALID_CONNECTION); EXPECT_EQ(conn->RemoveDeviceData(), -E_INVALID_CONNECTION); std::map> tableMap; EXPECT_EQ(conn->RemoveExceptDeviceData(tableMap), -E_INVALID_CONNECTION); diff --git a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp index 4d613fd0852d87a71b69daa8b040133f6a705e72..684b8bbe1d0f2c524b7758a638a63826efb56fa5 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp @@ -261,6 +261,32 @@ HWTEST_F(DistributedDBBasicRDBTest, RdbCloudSyncExample002, TestSize.Level0) EXPECT_EQ(RDBGeneralUt::CountTableData(info1, g_defaultTable1), 20); } +/** + * @tc.name: RdbCloudSyncExample003 + * @tc.desc: Test update data will change cursor. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBBasicRDBTest, RdbCloudSyncExample003, TestSize.Level0) +{ + /** + * @tc.steps: step1. cloud insert data. + * @tc.expected: step1. Ok + */ + auto info1 = GetStoreInfo1(); + ASSERT_EQ(BasicUnitTest::InitDelegate(info1, g_deviceA), E_OK); + ASSERT_EQ(SetDistributedTables(info1, {g_defaultTable1}, TableSyncType::CLOUD_COOPERATION), E_OK); + auto ret = ExecuteSQL("INSERT INTO defaultTable1(id, name) VALUES(1, 'name1')", info1); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info1, DBCommon::GetLogTableName(g_defaultTable1), "cursor >= 1"), 1); + ret = ExecuteSQL("UPDATE defaultTable1 SET name='name1' WHERE id=1", info1); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info1, DBCommon::GetLogTableName(g_defaultTable1), "cursor >= 2"), 1); + ret = ExecuteSQL("UPDATE defaultTable1 SET name='name2' WHERE id=1", info1); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info1, DBCommon::GetLogTableName(g_defaultTable1), "cursor >= 3"), 1); +} + /** * @tc.name: RdbCloudSyncExample004 * @tc.desc: Test upload failed, when return FILE_NOT_FOUND diff --git a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_conflict_handler_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_conflict_handler_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa750d6b17fe5b71fec7158b0e4e1aed9b7564ca --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_conflict_handler_test.cpp @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rdb_general_ut.h" + +#ifdef USE_DISTRIBUTEDDB_CLOUD +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { +class DistributedDBRDBConflictHandlerTest : public RDBGeneralUt { +public: + void SetUp() override; + void TearDown() override; +protected: + static constexpr const char *CLOUD_SYNC_TABLE_A = "CLOUD_SYNC_TABLE_A"; + void InitTables(const std::string &table = CLOUD_SYNC_TABLE_A); + void InitSchema(const std::string &table = CLOUD_SYNC_TABLE_A); + void InitDistributedTable(const std::vector &tables = {CLOUD_SYNC_TABLE_A}); + void Store1InsertStore2Pull(); + void Store1InsertStore2Pull(const Query &pushQuery); + void Store1DeleteStore2Pull(); + void AbortCloudSyncTest(const std::function &forkFunc, const std::function &cancelForkFunc, + SyncMode mode); + void PrepareEnv(const UtDateBaseSchemaInfo &info); + StoreInfo info1_ = {USER_ID, APP_ID, STORE_ID_1}; + StoreInfo info2_ = {USER_ID, APP_ID, STORE_ID_2}; +}; + +void DistributedDBRDBConflictHandlerTest::SetUp() +{ + RDBGeneralUt::SetUp(); + // create db first + EXPECT_EQ(BasicUnitTest::InitDelegate(info1_, "dev1"), E_OK); + EXPECT_EQ(BasicUnitTest::InitDelegate(info2_, "dev2"), E_OK); + EXPECT_NO_FATAL_FAILURE(InitTables()); + InitSchema(); + InitDistributedTable(); +} + +void DistributedDBRDBConflictHandlerTest::TearDown() +{ + RDBGeneralUt::TearDown(); +} + +void DistributedDBRDBConflictHandlerTest::InitTables(const std::string &table) +{ + std::string sql = "CREATE TABLE IF NOT EXISTS " + table + "(" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "intCol INTEGER, stringCol1 TEXT, stringCol2 TEXT, uuidCol TEXT UNIQUE)"; + EXPECT_EQ(ExecuteSQL(sql, info1_), E_OK); + EXPECT_EQ(ExecuteSQL(sql, info2_), E_OK); +} + +void DistributedDBRDBConflictHandlerTest::InitSchema(const std::string &table) +{ + const std::vector filedInfo = { + {{"intCol", TYPE_INDEX, false, true}, false}, + {{"stringCol1", TYPE_INDEX, false, true}, false}, + {{"uuidCol", TYPE_INDEX, true, true, true}, false}, + }; + UtDateBaseSchemaInfo schemaInfo = { + .tablesInfo = { + {.name = table, .fieldInfo = filedInfo} + } + }; + RDBGeneralUt::SetSchemaInfo(info1_, schemaInfo); + RDBGeneralUt::SetSchemaInfo(info2_, schemaInfo); +} + +void DistributedDBRDBConflictHandlerTest::InitDistributedTable(const std::vector &tables) +{ + RDBGeneralUt::SetCloudDbConfig(info1_); + RDBGeneralUt::SetCloudDbConfig(info2_); + ASSERT_EQ(SetDistributedTables(info1_, tables, TableSyncType::CLOUD_COOPERATION), E_OK); + ASSERT_EQ(SetDistributedTables(info2_, tables, TableSyncType::CLOUD_COOPERATION), E_OK); +} + +void DistributedDBRDBConflictHandlerTest::Store1InsertStore2Pull() +{ + Query pushQuery = Query::Select().From(CLOUD_SYNC_TABLE_A).EqualTo("stringCol2", "text2"); + Store1InsertStore2Pull(pushQuery); +} + +void DistributedDBRDBConflictHandlerTest::Store1InsertStore2Pull(const Query &pushQuery) +{ + // step1 store1 insert (id=1, intCol=1, stringCol1='text1', stringCol2='text2', uuidCol='uuid1') + auto ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(1, 1, 'text1', 'text2', 'uuid1')", info1_); + ASSERT_EQ(ret, E_OK); + // step2 check insert success + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='text2'"), 1); + // step3 store1 push (intCol=1, stringCol1='text1', uuidCol='uuid1') to cloud + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, pushQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + // step4 store2 pull (intCol=1, stringCol1='text1', uuidCol='uuid1') from cloud + Query pullQuery = Query::Select().FromTable({CLOUD_SYNC_TABLE_A}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, pullQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL, OK, OK)); +} + +void DistributedDBRDBConflictHandlerTest::Store1DeleteStore2Pull() +{ + // step1 store1 delete (id=1, intCol=1, stringCol1='text1', stringCol2='text2', uuidCol='uuid1') + auto ret = ExecuteSQL("DELETE FROM CLOUD_SYNC_TABLE_A WHERE uuidCol='uuid1'", info1_); + ASSERT_EQ(ret, E_OK); + // step2 check delete success + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, "uuidCol='uuid1'"), 0); + // step3 store1 push and store2 pull + Query pushQuery = Query::Select().From(CLOUD_SYNC_TABLE_A).EqualTo("stringCol2", "text2"); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, pushQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + Query pullQuery = Query::Select().FromTable({CLOUD_SYNC_TABLE_A}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, pullQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL, OK, OK)); +} + +void DistributedDBRDBConflictHandlerTest::PrepareEnv(const UtDateBaseSchemaInfo &info) +{ + RDBGeneralUt::SetSchemaInfo(info1_, info); + RDBGeneralUt::SetSchemaInfo(info2_, info); + RDBGeneralUt::SetCloudDbConfig(info1_); + RDBGeneralUt::SetCloudDbConfig(info2_); + std::vector tables; + for (const auto &item : info.tablesInfo) { + tables.push_back(item.name); + } + ASSERT_EQ(SetDistributedTables(info1_, tables, TableSyncType::CLOUD_COOPERATION), E_OK); + ASSERT_EQ(SetDistributedTables(info2_, tables, TableSyncType::CLOUD_COOPERATION), E_OK); +} + +/** + * @tc.name: SimpleSync001 + * @tc.desc: Test store1 insert/delete local and custom push, store2 custom pull. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync001, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &old, const VBucket &, VBucket &) { + EXPECT_EQ(old.find(CloudDbConstant::CREATE_FIELD), old.end()); + EXPECT_EQ(old.find(CloudDbConstant::MODIFY_FIELD), old.end()); + EXPECT_EQ(old.find(CloudDbConstant::VERSION_FIELD), old.end()); + return ConflictRet::UPSERT; + }); + // step1 store1 push one row and store2 pull + Store1InsertStore2Pull(); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2 IS NULL"), 1); + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &old, const VBucket &, VBucket &) { + EXPECT_NE(old.find(CloudDbConstant::CREATE_FIELD), old.end()); + EXPECT_NE(old.find(CloudDbConstant::MODIFY_FIELD), old.end()); + return ConflictRet::DELETE; + }); + // step2 store1 delete one row and store2 pull + Store1DeleteStore2Pull(); + // step3 store2 check not exist uuidCol='uuid1' + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, "uuidCol='uuid1'"), 0); +} + +/** + * @tc.name: SimpleSync002 + * @tc.desc: Test store1 insert local and custom push, store2 custom pull to update it. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync002, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &old, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + Type expect = std::string("text3"); + EXPECT_EQ(old.at("stringCol2"), expect); + return ConflictRet::UPSERT; + }); + // step1 store1 insert (id=1, intCol=1, stringCol1='text1', stringCol2='text2', uuidCol='uuid1') + // store2 insert (id=2, intCol=1, stringCol1='text1', stringCol2='text3', uuidCol='uuid1') + auto ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(1, 1, 'text1', 'text2', 'uuid1')", info1_); + ASSERT_EQ(ret, E_OK); + ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(2, 1, 'text1', 'text3', 'uuid1')", info2_); + ASSERT_EQ(ret, E_OK); + // step2 check insert success + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='text2'"), 1); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='text3'"), 1); + // step3 store1 push (intCol=1, stringCol1='text1', uuidCol='uuid1') to cloud + Query query = Query::Select().From(CLOUD_SYNC_TABLE_A).EqualTo("stringCol2", "text2"); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, query, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + // step4 store2 pull (intCol=1, stringCol1='text1', uuidCol='uuid1') from cloud + query = Query::Select().FromTable({CLOUD_SYNC_TABLE_A}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, query, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL, OK, OK)); + // step5 store2 check (intCol=1, stringCol1='text1', uuidCol='uuid1') exist and stringCol2 is 'text3' + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='upsert'"), 1); + // make sure store2 only exist one row + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A), 1); +} + +/** + * @tc.name: SimpleSync003 + * @tc.desc: Test store1 insert and custom push, store2 custom pull with not handle. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync003, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + return ConflictRet::NOT_HANDLE; + }); + // step1 store1 push one row and store2 pull + Store1InsertStore2Pull(); + // step2 store2 has no data + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A), 0); +} + +/** + * @tc.name: SimpleSync004 + * @tc.desc: Test store2 custom pull delete data with not handle. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync004, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + return ConflictRet::UPSERT; + }); + // step1 store1 push one row and store2 pull + Store1InsertStore2Pull(); + // step2 store2 has one row and exist gid + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + return ConflictRet::NOT_HANDLE; + }); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, "uuidCol='uuid1'"), 1); + EXPECT_EQ(CountTableData(info2_, DBCommon::GetLogTableName(CLOUD_SYNC_TABLE_A), "cloud_gid != ''"), 1); + // step3 store1 delete one row and store2 pull + Store1DeleteStore2Pull(); + // step4 store2 check not exist uuidCol='uuid1' + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, "uuidCol='uuid1'"), 1); + EXPECT_EQ(CountTableData(info2_, DBCommon::GetLogTableName(CLOUD_SYNC_TABLE_A), + "cloud_gid = '' AND version = '' "), 1); +} + +/** + * @tc.name: SimpleSync005 + * @tc.desc: Test store1 custom push with not equal to. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync005, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + upsert.insert({"stringCol1", std::string("upsert")}); + return ConflictRet::UPSERT; + }); + // step1 store1 insert (id=2, intCol=2, stringCol1='text2', stringCol2='text3', uuidCol='uuid2') + auto ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(2, 2, 'text2', 'text3', 'uuid2')", info1_); + ASSERT_EQ(ret, E_OK); + // step2 check insert success + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, + "intCol=2 AND stringCol1='text2' AND uuidCol='uuid2' AND stringCol2='text3'"), 1); + // step3 store1 insert (id=1, intCol=1, stringCol1='text1', stringCol2='text2', uuidCol='uuid2') + // store1 push data with stringCol2 not equal to 'text3' + Query pushQuery = Query::Select().From(CLOUD_SYNC_TABLE_A).NotEqualTo("stringCol2", "text2"); + Store1InsertStore2Pull(pushQuery); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2 IS NULL"), 0); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=2 AND stringCol1='upsert' AND uuidCol='uuid2' AND stringCol2='upsert'"), 1); +} + +/** + * @tc.name: SimpleSync006 + * @tc.desc: Test set both primary key and unique column in the schema and sync. + * @tc.type: FUNC + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync006, TestSize.Level0) +{ + /** + * @tc.steps:step1. Prepare table + * @tc.expected: step1. Return OK. + */ + std::string tableName = "CLOUD_SYNC_TABLE_B"; + std::string sql = "CREATE TABLE IF NOT EXISTS " + tableName + "(" + "id INTEGER PRIMARY KEY NOT NULL, intCol INTEGER, stringCol1 TEXT, stringCol2 TEXT, uuidCol TEXT UNIQUE)"; + EXPECT_EQ(ExecuteSQL(sql, info1_), E_OK); + EXPECT_EQ(ExecuteSQL(sql, info2_), E_OK); + const std::vector filedInfo = { + {{"id", TYPE_INDEX, true, false}, false}, + {{"intCol", TYPE_INDEX, false, true}, false}, + {{"stringCol1", TYPE_INDEX, false, true}, false}, + {{"uuidCol", TYPE_INDEX, false, true, true}, false}, + {{"nonExist", TYPE_INDEX, false, true}, false}, + }; + UtDateBaseSchemaInfo schemaInfo = { + .tablesInfo = { + {.name = tableName, .fieldInfo = filedInfo} + } + }; + PrepareEnv(schemaInfo); + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::UPSERT; + }); + + /** + * @tc.steps:step2. Prepare data and sync + * @tc.expected: step2. Return OK. + */ + sql = "INSERT INTO " + tableName + " VALUES(1, 1, 'text1', 'text2', 'uuid1')"; + auto ret = ExecuteSQL(sql, info1_); + ASSERT_EQ(ret, E_OK); + sql = "INSERT INTO " + tableName + " VALUES(1, 1, 'text3', 'text4', 'uuid1')"; + ret = ExecuteSQL(sql, info2_); + ASSERT_EQ(ret, E_OK); + + Query query = Query::Select().FromTable({tableName}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, query, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, query, SyncMode::SYNC_MODE_CLOUD_MERGE, OK, OK)); + EXPECT_EQ(CountTableData(info1_, tableName, + "id = 1 AND intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='text2'"), 0); + EXPECT_EQ(CountTableData(info1_, tableName, + "id = 1 AND intCol=1 AND stringCol1='text3' AND uuidCol='uuid1' AND stringCol2='text2'"), 1); +} + +/** + * @tc.name: SimpleSync007 + * @tc.desc: Test unique col with null data. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync007, TestSize.Level0) +{ + const std::string table = "SimpleSync007"; + InitTables(table); + InitSchema(table); + /** + * @tc.steps:step1. Prepare data which unique col is null + * @tc.expected: step1. Return OK. + */ + auto sql = "INSERT INTO " + table + " VALUES(1, 1, 'text1', 'text2', null)"; + auto ret = ExecuteSQL(sql, info1_); + ASSERT_EQ(ret, E_OK); + /** + * @tc.steps:step2. Create distributed table with null data + * @tc.expected: step2. Create OK. + */ + ASSERT_NO_FATAL_FAILURE(InitDistributedTable({table})); + /** + * @tc.steps:step3. Update data and set uuid + * @tc.expected: step3. Update OK. + */ + sql = "UPDATE " + table + " SET uuidCol='123' WHERE intCol=1"; + ret = ExecuteSQL(sql, info1_); + ASSERT_EQ(ret, E_OK); + /** + * @tc.steps:step4. Store1 sync to store2 + * @tc.expected: step4. Sync OK. + */ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + return ConflictRet::UPSERT; + }); + Query pushQuery = Query::Select().From(table).EqualTo("intCol", 1); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, pushQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + Query pullQuery = Query::Select().FromTable({table}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, pullQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL, OK, OK)); + EXPECT_EQ(CountTableData(info2_, table, + "id = 1 AND intCol=1 AND stringCol1='text1' AND uuidCol='123' AND stringCol2 IS NULL"), 1); +} + +/** + * @tc.name: SimpleSync008 + * @tc.desc: Test save sync data when local has null data. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync008, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::UPSERT; + }); + /** + * @tc.steps:step1. Prepare data which unique col is null + * @tc.expected: step1. Return OK. + */ + auto sql = "INSERT INTO " + std::string(CLOUD_SYNC_TABLE_A) + " VALUES(1, 1, 'text1', 'text2', null)"; + auto ret = ExecuteSQL(sql, info2_); + ASSERT_EQ(ret, E_OK); + /** + * @tc.steps:step2. Store1 push one row and store2 pull + * @tc.expected: step2. Sync OK. + */ + Store1InsertStore2Pull(); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2 IS NULL"), 1); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND stringCol2='text2' AND uuidCol IS NULL"), 1); +} + +/** + * @tc.name: SimpleSync009 + * @tc.desc: Test save sync data with integrate. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, SimpleSync009, TestSize.Level0) +{ + SetCloudConflictHandler(info2_, [](const std::string &, const VBucket &, const VBucket &, VBucket &update) { + update["stringCol1"] = std::string("INTEGRATE"); + return ConflictRet::INTEGRATE; + }); + SetCloudConflictHandler(info1_, [](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::UPSERT; + }); + /** + * @tc.steps:step1. Prepare data which unique col is uuid1 + * @tc.expected: step1. Return OK. + */ + auto sql = "INSERT INTO " + std::string(CLOUD_SYNC_TABLE_A) + " VALUES(1, 1, 'text1', 'text2', 'uuid1')"; + auto ret = ExecuteSQL(sql, info2_); + ASSERT_EQ(ret, E_OK); + /** + * @tc.steps:step2. Store1 push one row and store2 pull + * @tc.expected: step2. Sync OK. + */ + Store1InsertStore2Pull(); + EXPECT_EQ(CountTableData(info2_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='INTEGRATE' AND uuidCol='uuid1'"), 1); + /** + * @tc.steps:step3. Store2 push and store1 pull + * @tc.expected: step3. Sync OK. + */ + Query pushQuery = Query::Select().From({CLOUD_SYNC_TABLE_A}).EqualTo("stringCol1", "INTEGRATE"); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, pushQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PUSH, OK, OK)); + Query pullQuery = Query::Select().FromTable({CLOUD_SYNC_TABLE_A}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, pullQuery, SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL, OK, OK)); + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='INTEGRATE' AND uuidCol='uuid1'"), 1); +} + +void DistributedDBRDBConflictHandlerTest::AbortCloudSyncTest(const std::function &forkFunc, + const std::function &cancelForkFunc, SyncMode mode) +{ + /** + * @tc.steps:step1. Prepare data + * @tc.expected: step1. Return OK. + */ + auto ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(1, 2, 'text1', 'text3', 'uuid1')", info2_); + ASSERT_EQ(ret, E_OK); + ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(1, 1, 'text1', 'text2', 'uuid1')", info1_); + ASSERT_EQ(ret, E_OK); + ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(2, 1, 'text1', 'text3', 'uuid2')", info1_); + ASSERT_EQ(ret, E_OK); + /** + * @tc.steps:step2. Stop task when Query cloud data. + * @tc.expected: step2. Return OK. + */ + Query query = Query::Select().FromTable({CLOUD_SYNC_TABLE_A}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info2_, query, SYNC_MODE_CLOUD_MERGE, OK, OK)); + forkFunc(); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, query, mode, OK, TASK_INTERRUPTED)); + /** + * @tc.steps:step3. Sync again. + * @tc.expected: step3. Return OK. + */ + cancelForkFunc(); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info1_, query, mode, OK, OK)); +} + +/** + * @tc.name: AbortCloudSyncTest001 + * @tc.desc: Test abort cloud sync task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, AbortCloudSyncTest001, TestSize.Level0) +{ + std::shared_ptr virtualCloudDb = RDBGeneralUt::GetVirtualCloudDb(); + auto delegate = GetDelegate(info1_); + std::function forkFunc = [&virtualCloudDb, &delegate]() { + virtualCloudDb->ForkQuery([&delegate](const std::string &, VBucket &) { + EXPECT_EQ(delegate->StopTask(TaskType::BACKGROUND_TASK), OK); + }); + }; + std::function cancelForkFunc = [&virtualCloudDb]() { + virtualCloudDb->ForkQuery(nullptr); + }; + AbortCloudSyncTest(forkFunc, cancelForkFunc, SYNC_MODE_CLOUD_MERGE); +} + +/** + * @tc.name: AbortCloudSyncTest002 + * @tc.desc: Test abort cloud sync task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, AbortCloudSyncTest002, TestSize.Level0) +{ + auto delegate = GetDelegate(info1_); + auto handler = std::make_shared(); + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + return ConflictRet::UPSERT; + }); + EXPECT_EQ(delegate->SetCloudConflictHandler(handler), OK); + + std::shared_ptr virtualCloudDb = RDBGeneralUt::GetVirtualCloudDb(); + std::function forkFunc = [&virtualCloudDb, &delegate]() { + virtualCloudDb->ForkQuery([&delegate](const std::string &, VBucket &) { + EXPECT_EQ(delegate->StopTask(TaskType::BACKGROUND_TASK), OK); + }); + }; + std::function cancelForkFunc = [&virtualCloudDb]() { + virtualCloudDb->ForkQuery(nullptr); + }; + AbortCloudSyncTest(forkFunc, cancelForkFunc, SYNC_MODE_CLOUD_CUSTOM_PULL); +} + +/** + * @tc.name: AbortCloudSyncTest003 + * @tc.desc: Test abort cloud sync task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, AbortCloudSyncTest003, TestSize.Level0) +{ + auto delegate = GetDelegate(info1_); + std::shared_ptr virtualCloudDb = RDBGeneralUt::GetVirtualCloudDb(); + bool isStopTask = false; + std::function forkFunc = [&virtualCloudDb, &isStopTask, &delegate]() { + virtualCloudDb->ForkUpload([&isStopTask, &delegate](const std::string &, VBucket &) { + if (isStopTask) { + return; + } + EXPECT_EQ(delegate->StopTask(TaskType::BACKGROUND_TASK), OK); + isStopTask = true; + }); + }; + std::function cancelForkFunc = [&virtualCloudDb]() { + virtualCloudDb->ForkUpload(nullptr); + }; + AbortCloudSyncTest(forkFunc, cancelForkFunc, SYNC_MODE_CLOUD_MERGE); +} + +/** + * @tc.name: AbortCloudSyncTest004 + * @tc.desc: Test abort cloud sync task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, AbortCloudSyncTest004, TestSize.Level0) +{ + auto delegate = GetDelegate(info1_); + auto handler = std::make_shared(); + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &upsert) { + upsert.insert({"stringCol2", std::string("upsert")}); + return ConflictRet::UPSERT; + }); + EXPECT_EQ(delegate->SetCloudConflictHandler(handler), OK); + + std::shared_ptr virtualCloudDb = RDBGeneralUt::GetVirtualCloudDb(); + bool isStopTask = false; + std::function forkFunc = [&virtualCloudDb, &isStopTask, &delegate]() { + virtualCloudDb->ForkUpload([&isStopTask, &delegate](const std::string &, VBucket &) { + if (isStopTask) { + return; + } + EXPECT_EQ(delegate->StopTask(TaskType::BACKGROUND_TASK), OK); + isStopTask = true; + }); + }; + std::function cancelForkFunc = [&virtualCloudDb]() { + virtualCloudDb->ForkUpload(nullptr); + }; + AbortCloudSyncTest(forkFunc, cancelForkFunc, SYNC_MODE_CLOUD_CUSTOM_PUSH); +} + +/** + * @tc.name: AbortCloudSyncTest005 + * @tc.desc: Test abort cloud sync task. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, AbortCloudSyncTest005, TestSize.Level0) +{ + auto delegate = GetDelegate(info1_); + std::shared_ptr virtualCloudDb = RDBGeneralUt::GetVirtualCloudDb(); + std::function forkFunc = [&virtualCloudDb, &delegate]() { + virtualCloudDb->ForkBeforeBatchUpdate([&delegate](const std::string &, std::vector &, + std::vector &, bool) { + EXPECT_EQ(delegate->StopTask(TaskType::BACKGROUND_TASK), OK); + }); + }; + std::function cancelForkFunc = [&virtualCloudDb]() { + virtualCloudDb->ForkUpload(nullptr); + }; + AbortCloudSyncTest(forkFunc, cancelForkFunc, SYNC_MODE_CLOUD_MERGE); +} + +/** + * @tc.name: LogTrigger001 + * @tc.desc: Test trigger update log after col change to null. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, LogTrigger001, TestSize.Level0) +{ + // step1 store1 insert (id=1, intCol=1, stringCol1='text1', stringCol2='text2', uuidCol='uuid1') + auto ret = ExecuteSQL("INSERT INTO CLOUD_SYNC_TABLE_A VALUES(1, 1, 'text1', 'text2', 'uuid1')", info1_); + ASSERT_EQ(ret, E_OK); + // step2 check insert success + EXPECT_EQ(CountTableData(info1_, CLOUD_SYNC_TABLE_A, + "intCol=1 AND stringCol1='text1' AND uuidCol='uuid1' AND stringCol2='text2'"), 1); + EXPECT_EQ(CountTableData(info1_, DBCommon::GetLogTableName(CLOUD_SYNC_TABLE_A), + "data_key=1 AND cursor=1"), 1); + + ret = ExecuteSQL("UPDATE CLOUD_SYNC_TABLE_A SET stringCol1='text2' WHERE uuidCol='uuid1'", info1_); + ASSERT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info1_, DBCommon::GetLogTableName(CLOUD_SYNC_TABLE_A), + "data_key=1 AND cursor=2"), 1); + + ret = ExecuteSQL("UPDATE CLOUD_SYNC_TABLE_A SET stringCol1=null WHERE uuidCol='uuid1'", info1_); + ASSERT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info1_, DBCommon::GetLogTableName(CLOUD_SYNC_TABLE_A), + "data_key=1 AND cursor=3"), 1); +} + +/** + * @tc.name: LogTrigger002 + * @tc.desc: Test change pk with no dup check table. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, LogTrigger002, TestSize.Level0) +{ + /** + * @tc.steps:step1. Prepare table + * @tc.expected: step1. Return OK. + */ + std::string tableName = "CLOUD_SYNC_TABLE_B"; + std::string sql = "CREATE TABLE IF NOT EXISTS " + tableName + "(" + "id INTEGER PRIMARY KEY NOT NULL, intCol INTEGER, stringCol1 TEXT, stringCol2 TEXT)"; + EXPECT_EQ(ExecuteSQL(sql, info1_), E_OK); + EXPECT_EQ(ExecuteSQL(sql, info2_), E_OK); + const std::vector filedInfo = { + {{"id", TYPE_INDEX, true, false}, false}, + {{"intCol", TYPE_INDEX, false, true}, false}, + {{"stringCol1", TYPE_INDEX, false, true}, false}, + }; + UtDateBaseSchemaInfo schemaInfo = { + .tablesInfo = { + {.name = tableName, .fieldInfo = filedInfo} + } + }; + PrepareEnv(schemaInfo); + /** + * @tc.steps:step2. store2 insert data and sync to cloud + * @tc.expected: step2. Sync OK. + */ + sql = "INSERT INTO " + tableName + "(rowid, id, intCol, stringCol1, stringCol2) VALUES(1, 2, 1, 'text1', 'text2')"; + auto ret = ExecuteSQL(sql, info2_); + ASSERT_EQ(ret, E_OK); + sql = "UPDATE " + tableName + " SET id=1 WHERE intCol=1"; + ret = ExecuteSQL(sql, info2_); + ASSERT_EQ(ret, E_OK); + EXPECT_EQ(CountTableData(info2_, DBCommon::GetLogTableName(tableName)), 1); +} + +/** + * @tc.name: UpgradeTest001 + * @tc.desc: Test upgrade distributed table. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBConflictHandlerTest, UpgradeTest001, TestSize.Level0) +{ + const std::vector filedInfo = { + {{"intCol", TYPE_INDEX, false, true}, false}, + {{"stringCol1", TYPE_INDEX, false, true}, false}, + {{"stringCol2", TYPE_INDEX, false, true}, false}, + {{"uuidCol", TYPE_INDEX, false, true, true}, false}, + }; + UtDateBaseSchemaInfo schemaInfo = { + .tablesInfo = { + {.name = CLOUD_SYNC_TABLE_A, .fieldInfo = filedInfo} + } + }; + RDBGeneralUt::SetSchemaInfo(info1_, schemaInfo); + EXPECT_EQ(SetDistributedTables(info1_, {CLOUD_SYNC_TABLE_A}, TableSyncType::CLOUD_COOPERATION), E_OK); +} +} +#endif // USE_DISTRIBUTEDDB_CLOUD \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_syncer_test.h b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_syncer_test.h index cfd27ab9bb5bec538a536791be92e68053d5ed6f..f647c0dab22e69fadac21b800bbb2bf338d64cdb 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_syncer_test.h +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_syncer_test.h @@ -15,10 +15,10 @@ #ifndef CLOUDSYNCER_TEST_H #define CLOUDSYNCER_TEST_H -#include "cloud_merge_strategy.h" #include "cloud_syncer.h" #include "cloud_syncer_test.h" #include "cloud_sync_utils.h" +#include "cloud/strategy/cloud_merge_strategy.h" #include "mock_iclouddb.h" namespace DistributedDB { @@ -60,8 +60,8 @@ public: currentContext_.notifier = std::make_shared(this); currentContext_.processRecorder = std::make_shared(); currentContext_.notifier->Init({currentContext_.tableName}, { "cloud" }, cloudTaskInfos_[taskId].users); - currentContext_.strategy = std::make_shared(); - currentContext_.strategy->SetIsKvScene(isKvScene_); + strategyProxy_.UpdateStrategy(SyncMode::SYNC_MODE_CLOUD_MERGE, isKvScene_, + SingleVerConflictResolvePolicy::DEFAULT_LAST_WIN, std::weak_ptr()); closed_ = false; cloudTaskInfos_[taskId].callback = [this, taskId](const std::map &process) { if (process.size() >= 1u) { @@ -98,7 +98,7 @@ public: void CallClose() { currentContext_.currentTaskId = 0u; - currentContext_.strategy = nullptr; + strategyProxy_.ResetStrategy(); currentContext_.notifier = nullptr; Close(); } diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_db_proxy_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_db_proxy_test.cpp index d58d074600df3b4cf5c6a45dc675a1440f3c4ea7..8c041d91af09390092870205d7499e7f6c6db45a 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_db_proxy_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_db_proxy_test.cpp @@ -1074,6 +1074,37 @@ HWTEST_F(DistributedDBCloudDBProxyTest, CloudSyncUtilsTest, TestSize.Level0) EXPECT_EQ(utilsObj.IsChangeDataEmpty(changedData), false); } +/** + * @tc.name: CloudDBProxyTest017 + * @tc.desc: Test WaitAsyncGenLogTaskFinished + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudDBProxyTest, CloudDBProxyTest017, TestSize.Level0) +{ + /** + * @tc.steps: step1. Wait async gen log task finished when storage proxy is nullptr + * @tc.expected: step1. -E_INVALID_DB. + */ + auto cloudSyncer1 = new(std::nothrow) VirtualCloudSyncer(nullptr); + ASSERT_NE(cloudSyncer1, nullptr); + EXPECT_EQ(cloudSyncer1->WaitAsyncGenLogTaskFinished(0u), -E_INVALID_DB); + delete cloudSyncer1; + /** + * @tc.steps: step2. Wait async gen log task finished when task id is invalid + * @tc.expected: step2. -E_INVALID_DB. + */ + auto iCloud = std::make_shared(); + EXPECT_CALL(*iCloud, StartTransaction).WillRepeatedly(testing::Return(E_OK)); + EXPECT_CALL(*iCloud, Commit).WillRepeatedly(testing::Return(E_OK)); + EXPECT_CALL(*iCloud, GetIdentify).WillRepeatedly(testing::Return("CloudSyncerTest001")); + auto cloudSyncer2 = new(std::nothrow) VirtualCloudSyncer(StorageProxy::GetCloudDb(iCloud.get())); + ASSERT_NE(cloudSyncer2, nullptr); + EXPECT_EQ(cloudSyncer2->WaitAsyncGenLogTaskFinished(0u), -E_INVALID_DB); + delete cloudSyncer2; +} + /** * @tc.name: CloudDBProxyTest018 * @tc.desc: Test query all gid diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_strategy_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_strategy_test.cpp index 51efeed680312eae514d312c5464ced7c6f93e4d..8bf6e5bd81c83bb6781cfd96a9a329316e589587 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_strategy_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_strategy_test.cpp @@ -13,8 +13,9 @@ * limitations under the License. */ #include +#include "cloud/strategy/strategy_factory.h" #include "distributeddb_tools_unit_test.h" -#include "strategy_factory.h" +#include "rdb_general_ut.h" #include "virtual_cloud_syncer.h" using namespace std; @@ -227,6 +228,12 @@ HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest002, TestSize.Level0) */ localInfo.cloudGid = "gid"; EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::CLEAR_GID); + /** + * @tc.steps: step7. local data is locked + * @tc.expected: step7. CLEAR_GID + */ + localInfo.status = static_cast(LockStatus::LOCK); + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::LOCKED_NOT_HANDLE); } /** @@ -268,16 +275,24 @@ HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest003, TestSize.Level0) EXPECT_EQ(strategy->TagSyncDataStatus(false, false, localInfo, cloudInfo), OpType::DELETE); /** - * @tc.steps: step5. local exist cloud record and its deleted in cloud - * @tc.expected: step5. delete + * @tc.steps: step5. local not exist cloud record and it's exist in cloud (with gid) + * @tc.expected: step5. update + */ + cloudInfo.flag = 0x00; + EXPECT_EQ(strategy->TagSyncDataStatus(false, false, localInfo, cloudInfo), OpType::UPDATE); + + /** + * @tc.steps: step6. local exist cloud record and its deleted in cloud + * @tc.expected: step6. delete */ + cloudInfo.flag = 0x01; // it means delete EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::DELETE); localInfo.cloudGid = ""; EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::DELETE); /** - * @tc.steps: step6. local exist cloud record and its not deleted in cloud(WITH OR WITHOUT gid) - * @tc.expected: step6. UPDATE + * @tc.steps: step7. local exist cloud record and its not deleted in cloud(WITH OR WITHOUT gid) + * @tc.expected: step7. UPDATE */ cloudInfo.flag = 0x00; EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::UPDATE); @@ -310,6 +325,10 @@ HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest004, TestSize.Level0) cloudInfo.flag = 0x00; localInfo.flag = 0x01; EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::INSERT); + + localInfo.flag = 0x00; + strategy->SetConflictResolvePolicy(SingleVerConflictResolvePolicy::DENY_OTHER_DEV_AMEND_CUR_DEV_DATA); + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::NOT_HANDLE); } /** @@ -419,4 +438,181 @@ HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest007, TestSize.Level0) localInfo.flag = static_cast(LogInfoFlag::FLAG_CLOUD_WRITE); EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::NOT_HANDLE); } + +/** + * @tc.name: TagOpTyeTest008 + * @tc.desc: Verify cloud merge strategy when cloud win. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest008, TestSize.Level0) +{ + /** + * @tc.steps: step1. build cloud merge strategy + */ + auto strategy = StrategyFactory::BuildSyncStrategy(SyncMode::SYNC_MODE_CLOUD_MERGE); + ASSERT_NE(strategy, nullptr); + LogInfo localInfo; + LogInfo cloudInfo; + /** + * @tc.steps: step2. local exist and cloud exist + * @tc.expected: step2. insert cloud record to local + */ + EXPECT_EQ(strategy->TagSyncDataStatus(false, true, localInfo, cloudInfo), OpType::UPDATE); + /** + * @tc.steps: step3. local deleted and cloud exist + * @tc.expected: step3. insert cloud record to local + */ + localInfo.flag = static_cast(LogInfoFlag::FLAG_DELETE); + EXPECT_EQ(strategy->TagSyncDataStatus(false, true, localInfo, cloudInfo), OpType::INSERT); + /** + * @tc.steps: step4. local exist and cloud deleted + * @tc.expected: step4. insert cloud record to local + */ + localInfo.flag = 0; + cloudInfo.flag = static_cast(LogInfoFlag::FLAG_DELETE); + EXPECT_EQ(strategy->TagSyncDataStatus(false, true, localInfo, cloudInfo), OpType::DELETE); + /** + * @tc.steps: step5. local deleted and cloud deleted + * @tc.expected: step5. insert cloud record to local + */ + localInfo.flag = static_cast(LogInfoFlag::FLAG_DELETE); + EXPECT_EQ(strategy->TagSyncDataStatus(false, true, localInfo, cloudInfo), OpType::UPDATE_TIMESTAMP); +} + +/** + * @tc.name: TagOpTyeTest009 + * @tc.desc: Verify cloud force push strategy when local device name is "cloud". + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest009, TestSize.Level0) +{ + /** + * @tc.steps: step1. build cloud force push strategy + */ + auto strategy = StrategyFactory::BuildSyncStrategy(SyncMode::SYNC_MODE_CLOUD_FORCE_PUSH); + ASSERT_NE(strategy, nullptr); + LogInfo localInfo; + localInfo.device = "cloud"; + LogInfo cloudInfo; + localInfo.cloudGid = "gid"; + /** + * @tc.steps: step2. local timestamp equals cloud timestamp + * @tc.expected: step2. SET_CLOUD_FORCE_PUSH_FLAG_ONE + */ + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::SET_CLOUD_FORCE_PUSH_FLAG_ONE); + /** + * @tc.steps: step2. local timestamp not equals cloud timestamp + * @tc.expected: step2. SET_CLOUD_FORCE_PUSH_FLAG_ONE + */ + localInfo.timestamp = 1u; + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::SET_CLOUD_FORCE_PUSH_FLAG_ZERO); +} + +/** + * @tc.name: TagOpTyeTest010 + * @tc.desc: Verify cloud custom pull strategy. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest010, TestSize.Level0) +{ + /** + * @tc.steps: step1. create merge strategy but not set cloud conflict handler, init localInfo/cloudInfo. + * @tc.expected: step1. create ok + */ + auto strategy = StrategyFactory::BuildSyncStrategy(SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL); + ASSERT_NE(strategy, nullptr); + LogInfo localInfo; + LogInfo cloudInfo; + VBucket localData; + VBucket cloudData; + DataStatusInfo statusInfo; + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), OpType::NOT_HANDLE); + /** + * @tc.steps: step2. set cloud conflict handler but return BUTT. + * @tc.expected: step2. create ok + */ + auto handler = std::make_shared(); + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::BUTT; + }); + strategy->SetCloudConflictHandler(handler); + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), OpType::NOT_HANDLE); + /** + * @tc.steps: step3. set cloud conflict handler and return UPSERT. + * @tc.expected: step3. create ok + */ + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::UPSERT; + }); + strategy->SetCloudConflictHandler(handler); + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), OpType::INSERT); + statusInfo.isExistInLocal = true; + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), OpType::UPDATE); +} + +/** + * @tc.name: TagOpTyeTest011 + * @tc.desc: Verify cloud strategy with policy DENY_OTHER_DEV_AMEND_CUR_DEV_DATA. + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest011, TestSize.Level0) +{ + auto strategy = StrategyFactory::BuildSyncStrategy(SyncMode::SYNC_MODE_CLOUD_MERGE); + ASSERT_NE(strategy, nullptr); + strategy->SetConflictResolvePolicy(SingleVerConflictResolvePolicy::DENY_OTHER_DEV_AMEND_CUR_DEV_DATA); + LogInfo localInfo; + localInfo.flag = static_cast(LogInfoFlag::FLAG_DELETE); + LogInfo cloudInfo; + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::INSERT); + + localInfo.flag = 0; + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::NOT_HANDLE); + + localInfo.device = "dev"; + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::ONLY_UPDATE_GID); + localInfo.originDev = "originDev"; + EXPECT_EQ(strategy->TagSyncDataStatus(true, false, localInfo, cloudInfo), OpType::ONLY_UPDATE_GID); +} + +/** + * @tc.name: TagOpTyeTest012 + * @tc.desc: Verify custom pull strategy. + * @tc.type: FUNC + * @tc.author: zqq + */ +HWTEST_F(DistributedDBCloudStrategyTest, TagOpTyeTest012, TestSize.Level0) +{ + auto strategy = StrategyFactory::BuildSyncStrategy(SyncMode::SYNC_MODE_CLOUD_CUSTOM_PULL); + ASSERT_NE(strategy, nullptr); + auto handler = std::make_shared(); + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::UPSERT; + }); + strategy->SetCloudConflictHandler(handler); + DataStatusInfo statusInfo; + VBucket localData; + VBucket cloudData; + LogInfo localInfo; + LogInfo cloudInfo; + statusInfo.isExistInLocal = true; + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), + OpType::UPDATE); + handler->SetCallback([](const std::string &, const VBucket &, const VBucket &, VBucket &) { + return ConflictRet::NOT_HANDLE; + }); + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), + OpType::ONLY_UPDATE_GID); + localInfo.cloudGid = "1"; + cloudInfo.version = "v"; + EXPECT_EQ(strategy->TagSyncDataStatus(statusInfo, localInfo, localData, cloudInfo, cloudData), + OpType::ONLY_UPDATE_GID); +} } \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_download_assets_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_download_assets_test.cpp index e4f428beb1d0d3e806602c200512091fe6d406fa..04109dfd93e7b66a9bf07b6eb04fee6c5bf4b5d7 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_download_assets_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_download_assets_test.cpp @@ -626,6 +626,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest001(bool needDown InitDataStatusTest(needDownload); CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK); WaitForSync(count); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest003() @@ -659,6 +660,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest003() CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK); WaitForSync(count); g_virtualAssetLoader->ForkDownload(nullptr); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest004() @@ -704,6 +706,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest004() WaitForSync(count); g_virtualAssetLoader->ForkDownload(nullptr); g_virtualCloudDb->ForkQuery(nullptr); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest005() @@ -739,6 +742,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest005() CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK); WaitForSync(count); g_virtualAssetLoader->ForkDownload(nullptr); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest006() @@ -776,6 +780,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest006() CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK); WaitForSync(count); g_virtualAssetLoader->ForkDownload(nullptr); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest007() @@ -805,6 +810,7 @@ void DistributedDBCloudSyncerDownloadAssetsTest::DataStatusTest007() InitDataStatusTest(true); CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK, DBStatus::CLOUD_ERROR); WaitForSync(count); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } /* @@ -2086,6 +2092,7 @@ HWTEST_F(DistributedDBCloudSyncerDownloadAssetsTest, ConsistentFlagTest003, Test CallSync({ASSETS_TABLE_NAME}, SYNC_MODE_CLOUD_MERGE, DBStatus::OK); WaitForSync(count); CheckConsistentCount(db, localCount); + g_cloudStoreHook->SetSyncFinishHook(nullptr); } /** diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_lock_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_lock_test.cpp index 4db605eac429819f12143a631f367bb4fa40bae3..1eeb50c8c37a2ff45cd08b3bd495e3c4408d0ddc 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_lock_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_lock_test.cpp @@ -162,6 +162,9 @@ void DistributedDBCloudSyncerLockTest::SetUp(void) void DistributedDBCloudSyncerLockTest::TearDown(void) { + g_cloudStoreHook->SetSyncFinishHook(nullptr); + g_cloudStoreHook->SetDoUploadHook(nullptr); + g_cloudStoreHook->SetBeforeUploadTransaction(nullptr); RefObject::DecObjRef(g_store); g_virtualCloudDb->ForkUpload(nullptr); CloseDb(); diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_upload_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_upload_test.cpp index 1e13d3fef8416f09bcf17666f698626977e364a9..4be1082bc49746acef28032a35a25eb3abb5bd35 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_upload_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_syncer_upload_test.cpp @@ -145,65 +145,6 @@ void DistributedDBCloudSyncerUploadTest::CheckUploadFinish(TestCloudSyncer &clou EXPECT_TRUE(recorder->IsUploadFinish(0, cloudSyncer.GetCurrentContextTableName())); } -/** - * @tc.name: UploadModeCheck001 - * @tc.desc: Test different strategies of sync task call DoUpload() - * @tc.type: FUNC - * @tc.require: - * @tc.author: huangboxin - */ -HWTEST_F(DistributedDBCloudSyncerUploadTest, UploadModeCheck001, TestSize.Level1) -{ - MockICloudSyncStorageInterface *iCloud = new MockICloudSyncStorageInterface(); - std::shared_ptr storageProxy = std::make_shared(iCloud); - TestCloudSyncer *cloudSyncer = new(std::nothrow) TestCloudSyncer(storageProxy); - std::shared_ptr idb = std::make_shared(); - cloudSyncer->SetMockICloudDB(idb); - TaskId taskId = 1; - - EXPECT_CALL(*iCloud, GetMetaData(_, _)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, ChkSchema(_)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, StartTransaction(_, false)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, GetUploadCount(_, _, _, _, _)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, Commit(false)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, Rollback(false)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, GetCloudTableSchema(_, _)).WillRepeatedly(Return(E_OK)); - EXPECT_CALL(*iCloud, GetCloudData(_, _, _, _, _)).WillRepeatedly(Return(E_OK)); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_PUSH_ONLY); - int errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, -E_INVALID_ARGS); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_PULL_ONLY); - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, -E_INVALID_ARGS); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_PUSH_PULL); - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, -E_INVALID_ARGS); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_CLOUD_MERGE); - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, E_OK); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_CLOUD_FORCE_PUSH); - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, E_OK); - - cloudSyncer->InitCloudSyncer(taskId, SYNC_MODE_CLOUD_FORCE_PULL); - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, -E_INVALID_ARGS); - - errCode = cloudSyncer->CallDoUpload(taskId); - EXPECT_EQ(errCode, -E_INVALID_ARGS); - cloudSyncer->CallClose(); - RefObject::KillAndDecObjRef(cloudSyncer); - - storageProxy.reset(); - delete iCloud; - idb = nullptr; -} - /** * @tc.name: UploadModeCheck002 * @tc.desc: Test case1 about getting water mark diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/mock_icloud_sync_storage_interface.h b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/mock_icloud_sync_storage_interface.h index 214815522a96f6c5d88360be5a603293199f4af8..40c18ecfe8a403cb9114639c4ae92c6a0da97a39 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/mock_icloud_sync_storage_interface.h +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/mock_icloud_sync_storage_interface.h @@ -59,6 +59,7 @@ public: MOCK_METHOD2(GetDownloadAssetRecords, std::pair>(const std::string &, int64_t)); MOCK_METHOD0(IsExistTableContainAssets, bool(void)); MOCK_METHOD2(ConvertLogToLocal, int(const std::string &, const std::vector &)); + MOCK_METHOD1(WaitAsyncGenLogTaskFinished, int(const std::vector &)); }; } diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.cpp index 21d970dbbbcbd561e5a1e7e0588c3eb6cf5c5963..13965fa2aebac2508a97d47897ec0e699a636220 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.cpp @@ -33,7 +33,7 @@ int VirtualCloudSyncer::DoDownload(CloudSyncer::TaskId taskId, bool isFirstDownl return CloudSyncer::DoDownload(taskId, isFirstDownload); } -int VirtualCloudSyncer::DoDownloadInNeed(const CloudTaskInfo &taskInfo, const bool needUpload, bool isFirstDownload) +int VirtualCloudSyncer::DoDownloadInNeed(const CloudTaskInfo &taskInfo, bool needUpload, bool isFirstDownload) { if (!doDownload_) { LOGI("[VirtualCloudSyncer] download just return ok"); @@ -114,7 +114,12 @@ int VirtualCloudSyncer::CallTagStatusByStrategy(bool isExist, const DataInfoWith DataInfo dataInfo; dataInfo.localInfo = localInfo; dataInfo.cloudLogInfo = cloudInfo; - return CloudSyncer::TagStatusByStrategy(isExist, param, dataInfo, strategyOpResult); + param.downloadData.data.push_back({}); + std::lock_guard autoLock(dataLock_); + int errCode = E_OK; + std::tie(errCode, strategyOpResult) = + strategyProxy_.TagStatusByStrategy(isExist, 0, storageProxy_, param, dataInfo); + return errCode; } void VirtualCloudSyncer::PauseCurrentTask() @@ -136,4 +141,9 @@ void VirtualCloudSyncer::WaitTaskFinished() { CloudSyncer::WaitCurTaskFinished(); } + +int VirtualCloudSyncer::WaitAsyncGenLogTaskFinished(TaskId triggerTaskId) +{ + return CloudSyncer::WaitAsyncGenLogTaskFinished(triggerTaskId); +} } \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.h b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.h index 8b2479bde8e4b762adf40fc639ee4a9182ab3edd..c71e0e033383f0034b01c03f5669e8d49ab7f473 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.h +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/virtual_cloud_syncer.h @@ -24,7 +24,7 @@ public: int DoDownload(TaskId taskId, bool isFirstDownload) override; - int DoDownloadInNeed(const CloudTaskInfo &taskInfo, const bool needUpload, bool isFirstDownload) override; + int DoDownloadInNeed(const CloudTaskInfo &taskInfo, bool needUpload, bool isFirstDownload) override; int DoUpload(TaskId taskId, bool lastTable, LockAction lockAction) override; @@ -52,6 +52,8 @@ public: void TriggerAsyncTask(); void WaitTaskFinished(); + + int WaitAsyncGenLogTaskFinished(TaskId triggerTaskId); private: std::function downloadFunc_; std::function downloadInNeedFunc_;