diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp index 7fdc7d3741426bbaab2916a2bedb257a5da72464..2c00c81758b73381b61c253b1f5fbc753104f251 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.cpp @@ -563,21 +563,21 @@ static bool IsDataContainField(const std::string &assetFieldName, VBucket &data) } // AssetOpType and AssetStatus will be tagged, assets to be changed will be returned -static Assets TagAsset(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData, - bool setNormalStatus) +static int TagAsset(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData, + bool setNormalStatus, Assets &res) { - Assets res = {}; + res.clear(); bool beCoveredHasAsset = IsDataContainField(assetFieldName, beCoveredData) || IsDataContainField(assetFieldName, beCoveredData); bool coveredHasAsset = IsDataContainField(assetFieldName, coveredData); if (!beCoveredHasAsset) { if (!coveredHasAsset) { LOGD("Both data do not contain certain asset field"); - return res; + return E_OK; } TagAssetWithNormalStatus( setNormalStatus, AssetOpType::INSERT, std::get(coveredData[assetFieldName]), res); - return res; + return E_OK; } if (!coveredHasAsset) { if (beCoveredData[assetFieldName].index() == TYPE_INDEX) { @@ -588,7 +588,7 @@ static Assets TagAsset(const std::string &assetFieldName, VBucket &coveredData, TagAssetsWithNormalStatus(setNormalStatus, AssetOpType::DELETE, std::get(beCoveredData[assetFieldName]), res); } - return res; + return E_OK; } Asset &covered = std::get(coveredData[assetFieldName]); Assets beCoveredDataInAssets; @@ -605,50 +605,58 @@ static Assets TagAsset(const std::string &assetFieldName, VBucket &coveredData, if (covered.name != beCovered.name) { TagAssetWithNormalStatus(setNormalStatus, AssetOpType::INSERT, covered, res); TagAssetWithNormalStatus(setNormalStatus, AssetOpType::DELETE, beCovered, res); - return res; + return E_OK; } if (covered.hash != beCovered.hash) { TagAssetWithNormalStatus(setNormalStatus, AssetOpType::UPDATE, covered, res); } - return res; + return E_OK; } -static std::unordered_map GenAssetsMap(Assets &assets) +static int GenAssetsMap(Assets &assets, std::unordered_map &assetsMap) { - std::unordered_map assetsMap; + assetsMap.clear(); for (size_t i = 0; i < assets.size(); i++) { - assetsMap[assets[i].name] = i; + if (assetsMap.find(assets[i].name) == assetsMap.end()) { + assetsMap[assets[i].name] = i; + continue; + } + return -E_INVALID_ARGS; } - return assetsMap; + return E_OK; } // AssetOpType and AssetStatus will be tagged, assets to be changed will be returned // use VBucket rather than Type because we need to check whether it is empty -static Assets TagAssets(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData, - bool setNormalStatus) +static int TagAssets(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData, + bool setNormalStatus, Assets &res) { - Assets res = {}; + res.clear(); bool beCoveredHasAssets = IsDataContainField(assetFieldName, beCoveredData); bool coveredHasAssets = IsDataContainField(assetFieldName, coveredData); + std::unordered_map CoveredAssetsMap; + if (coveredHasAssets && GenAssetsMap(std::get(coveredData[assetFieldName]), CoveredAssetsMap) != E_OK) { + LOGW("Do not allow same name asset in assets field, we will ignore current record and return empty assets"); + return -E_INVALID_ARGS; + } if (!beCoveredHasAssets) { if (!coveredHasAssets) { - return res; + return E_OK; } // all the element in assets will be set to INSERT TagAssetsWithNormalStatus(setNormalStatus, AssetOpType::INSERT, std::get(coveredData[assetFieldName]), res); - return res; + return E_OK; } if (!coveredHasAssets) { // all the element in assets will be set to DELETE TagAssetsWithNormalStatus(setNormalStatus, AssetOpType::DELETE, std::get(beCoveredData[assetFieldName]), res); coveredData[assetFieldName] = res; - return res; + return E_OK; } Assets &covered = std::get(coveredData[assetFieldName]); Assets &beCovered = std::get(beCoveredData[assetFieldName]); - std::unordered_map CoveredAssetsMap = GenAssetsMap(covered); for (Asset &beCoveredAsset : beCovered) { auto it = CoveredAssetsMap.find(beCoveredAsset.name); if (it == CoveredAssetsMap.end()) { @@ -667,7 +675,7 @@ static Assets TagAssets(const std::string &assetFieldName, VBucket &coveredData, for (auto &noHandledAssetKvPair : CoveredAssetsMap) { TagAssetWithNormalStatus(setNormalStatus, AssetOpType::INSERT, covered[noHandledAssetKvPair.second], res); } - return res; + return E_OK; } bool CloudSyncer::ShouldProcessAssets() @@ -705,11 +713,11 @@ void CloudSyncer::WaitAllSyncCallbackTaskFinish() LOGD("[CloudSyncer] End wait all callback task finish"); } -std::map CloudSyncer::TagAssetsInSingleRecord(VBucket &CoveredData, VBucket &BeCoveredData, - bool setNormalStatus) +int CloudSyncer::TagAssetsInSingleRecord(VBucket &CoveredData, VBucket &BeCoveredData, bool setNormalStatus, + std::map &res) { // Define a map to store the result - std::map res = {}; + res.clear(); std::vector assetFields; { std::lock_guard autoLock(contextLock_); @@ -717,33 +725,37 @@ std::map CloudSyncer::TagAssetsInSingleRecord(VBucket &Cove } // For every column contain asset or assets, assetFields are in context for (const Field &assetField : assetFields) { - Assets assets = TagAssetsInSingleCol(CoveredData, BeCoveredData, assetField, setNormalStatus); + Assets assets; + int ret = TagAssetsInSingleCol(CoveredData, BeCoveredData, assetField, setNormalStatus, assets); + if (ret != E_OK) { + return ret; + } if (!assets.empty()) { res[assetField.colName] = assets; } } - return res; + return E_OK; } -Assets CloudSyncer::TagAssetsInSingleCol( - VBucket &CoveredData, VBucket &BeCoveredData, const Field &assetField, bool setNormalStatus) +int CloudSyncer::TagAssetsInSingleCol( + VBucket &CoveredData, VBucket &BeCoveredData, const Field &assetField, bool setNormalStatus, Assets &assets) { - // Define a list to store the tagged result - Assets assets = {}; + int ret = E_OK; + assets.clear(); // Define a list to store the tagged result switch (assetField.type) { case TYPE_INDEX: { - assets = TagAssets(assetField.colName, CoveredData, BeCoveredData, setNormalStatus); + ret = TagAssets(assetField.colName, CoveredData, BeCoveredData, setNormalStatus, assets); break; } case TYPE_INDEX: { - assets = TagAsset(assetField.colName, CoveredData, BeCoveredData, setNormalStatus); + ret = TagAsset(assetField.colName, CoveredData, BeCoveredData, setNormalStatus, assets); break; } default: LOGW("Meet an unexpected type %d", assetField.type); break; } - return assets; + return ret; } int CloudSyncer::FillCloudAssets( @@ -987,12 +999,12 @@ void CloudSyncer::TagDownloadAssets(size_t idx, const Type &prefix, SyncParam &p { case OpType::INSERT: case OpType::UPDATE: - assetsMap = TagAssetsInSingleRecord(param.downloadData.data[idx], localAssetInfo, false); + TagAssetsInSingleRecord(param.downloadData.data[idx], localAssetInfo, false, assetsMap); param.assetsDownloadList.downloadList.push_back( std::make_tuple(dataInfo.cloudLogInfo.cloudGid, prefix, strategy, assetsMap)); break; case OpType::DELETE: - assetsMap = TagAssetsInSingleRecord(param.downloadData.data[idx], localAssetInfo, false); + TagAssetsInSingleRecord(param.downloadData.data[idx], localAssetInfo, false, assetsMap); param.assetsDownloadList.completeDownloadList.push_back( std::make_tuple(dataInfo.cloudLogInfo.cloudGid, prefix, strategy, assetsMap)); break; @@ -1552,9 +1564,13 @@ void CloudSyncer::TagUploadAssets(CloudSyncData &uploadData) } // for delete scenario, assets should not appear in the records. Thereby we needn't tag the assests. // for insert scenario, gid does not exist. Thereby, we needn't compare with cloud asset get in download procedure + std::map localAssetChangeList; for (size_t i = 0; i < uploadData.insData.extend.size(); i++) { VBucket cloudAsset; // cloudAsset must be empty - (void)TagAssetsInSingleRecord(uploadData.insData.record[i], cloudAsset, true); + if (TagAssetsInSingleRecord(uploadData.insData.record[i], cloudAsset, true, localAssetChangeList) != E_OK) { + LOGW("There exist a record which contain same asset"); + continue; + } } // for update scenario, assets shoulb be compared with asset get in download procedure. for (size_t i = 0; i < uploadData.updData.extend.size(); i++) { @@ -1582,7 +1598,10 @@ void CloudSyncer::TagUploadAssets(CloudSyncData &uploadData) for (auto &it : cloudAssets[gid]) { cloudAsset[it.first] = it.second; } - (void)TagAssetsInSingleRecord(uploadData.updData.record[i], cloudAsset, true); + if (TagAssetsInSingleRecord(uploadData.updData.record[i], cloudAsset, true, localAssetChangeList) != E_OK) { + LOGW("There exist a record which contain same asset"); + continue; + } } } diff --git a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h index 2bda447e58555f3c75a8d3fbb23dc2d9254453bb..04427cf2f1b9e3fbc386efb260088862db38df3a 100644 --- a/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h +++ b/frameworks/libs/distributeddb/syncer/src/cloud/cloud_syncer.h @@ -225,11 +225,11 @@ protected: std::map GetAssetsFromVBucket(VBucket &data); - std::map TagAssetsInSingleRecord(VBucket &CoveredData, VBucket &BeCoveredData, - bool WriteToCoveredData); + int TagAssetsInSingleRecord(VBucket &CoveredData, VBucket &BeCoveredData, bool WriteToCoveredData, + std::map &res); - Assets TagAssetsInSingleCol(VBucket &CoveredData, VBucket &BeCoveredData, const Field &assetField, - bool WriteToCoveredData); + int TagAssetsInSingleCol(VBucket &CoveredData, VBucket &BeCoveredData, const Field &assetField, + bool WriteToCoveredData, Assets &assets); void TagStatus(bool isExist, SyncParam ¶m, size_t idx, DataInfo &dataInfo, VBucket &localAssetInfo); 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 c54616eb78919f7c3f521a893d4ce60ac5919fed..4159b77fad46244112bcc1737d33649e44e00fd0 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 @@ -226,7 +226,9 @@ public: std::map TestTagAssetsInSingleRecord( VBucket &CoveredData, VBucket &BeCoveredData, bool WriteToCoveredData = false) { - return TagAssetsInSingleRecord(CoveredData, BeCoveredData, WriteToCoveredData); + std::map downloadList; + TagAssetsInSingleRecord(CoveredData, BeCoveredData, WriteToCoveredData, downloadList); + return downloadList; } void SetCloudWaterMarks(const TableName &tableName, const CloudWaterMark &mark) diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_asset_compare_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_asset_compare_test.cpp index 4d9755fdc8069e9a24ff83f4facb4966a253efc2..76e2902faa069ebabb48eb9d76c602f0ab8cbe15 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_asset_compare_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/distributeddb_cloud_asset_compare_test.cpp @@ -76,6 +76,7 @@ namespace { VBucket DATA_ALL_NULL_ASSETS; VBucket DATA_EMPTY_ASSETS; VBucket DATA_UPDATE_DELTE_NOCHANGE_INSERT; + VBucket DATA_SAME_NAME_ASSETS; Asset GenAsset(std::string name, std::string hash) { @@ -128,6 +129,7 @@ namespace { DATA_EMPTY_ASSETS = GenDatum(15, "Lob6", a1, Assets({})); // id is 15 DATA_EMPTY_ASSETS.erase(FIELD_CARS); DATA_UPDATE_DELTE_NOCHANGE_INSERT = GenDatum(16, "Nico321", nil, Assets({a2Changed, a4, a5})); // id is 16 + DATA_SAME_NAME_ASSETS = GenDatum(16, "Nico156", nil, Assets({a1, a1Changed})); // id is 16 } void CreateDB() @@ -787,4 +789,41 @@ namespace { EXPECT_EQ(std::get(DATA_UPDATE_DELTE_NOCHANGE_INSERT[FIELD_CARS])[3].flag, static_cast(AssetOpType::DELETE)); } + + /** + * @tc.name: AssetCmpTest028 + * @tc.desc: Two same name asset appears in the assets field, this situation is not allowed + * @tc.type: FUNC + * @tc.require: + * @tc.author: wanyi + */ + HWTEST_F(DistributedDBCloudAssetCompareTest, AssetCmpTest028, TestSize.Level0) + { + auto assetList = g_cloudSyncer->TestTagAssetsInSingleRecord( + DATA_ALL_NULL_ASSETS, DATA_BASELINE, true); + EXPECT_EQ(std::get(DATA_ALL_NULL_ASSETS[FIELD_CARS])[0].flag, + static_cast(AssetOpType::DELETE)); + EXPECT_EQ(std::get(DATA_ALL_NULL_ASSETS[FIELD_CARS])[1].flag, + static_cast(AssetOpType::DELETE)); + EXPECT_EQ(std::get(DATA_ALL_NULL_ASSETS[FIELD_CARS])[2].flag, + static_cast(AssetOpType::DELETE)); + } + + /** + * @tc.name: AssetCmpTest029 + * @tc.desc: Two same name asset appears in the assets field, this situation is not allowed + * @tc.type: FUNC + * @tc.require: + * @tc.author: wanyi + */ + HWTEST_F(DistributedDBCloudAssetCompareTest, AssetCmpTest029, TestSize.Level0) + { + auto assetList = g_cloudSyncer->TestTagAssetsInSingleRecord( + DATA_SAME_NAME_ASSETS, DATA_EMPTY, false); + std::map expectedList; + expectedList[FIELD_HOUSE] = {}; + expectedList[FIELD_CARS] = {}; + ASSERT_TRUE(CheckAssetDownloadList(FIELD_HOUSE, assetList, expectedList)); + ASSERT_TRUE(CheckAssetDownloadList(FIELD_CARS, assetList, expectedList)); + } } \ No newline at end of file