1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "cloud/cloud_sync_tag_assets.h"
16
17 namespace DistributedDB {
18 namespace {
19
TagSingleAsset(AssetOpType flag,AssetStatus status,Asset & asset,Assets & res,int & errCode)20 void TagSingleAsset(AssetOpType flag, AssetStatus status, Asset &asset, Assets &res, int &errCode)
21 {
22 if (asset.status == static_cast<uint32_t>(AssetStatus::DELETE)) {
23 asset.flag = static_cast<uint32_t>(AssetOpType::DELETE);
24 } else {
25 asset.flag = static_cast<uint32_t>(flag);
26 }
27 asset.status = static_cast<uint32_t>(status);
28
29 Timestamp timestamp;
30 errCode = OS::GetCurrentSysTimeInMicrosecond(timestamp);
31 if (errCode != E_OK) {
32 LOGE("Can not get current timestamp.");
33 return;
34 }
35 asset.timestamp = static_cast<int64_t>(timestamp / CloudDbConstant::TEN_THOUSAND);
36 asset.status = asset.flag == static_cast<uint32_t>(AssetOpType::NO_CHANGE) ?
37 static_cast<uint32_t>(AssetStatus::NORMAL) : asset.status;
38 res.push_back(asset);
39 }
40
TagAssetWithNormalStatus(const bool isNormalStatus,AssetOpType flag,Asset & asset,Assets & res,int & errCode)41 void TagAssetWithNormalStatus(const bool isNormalStatus, AssetOpType flag,
42 Asset &asset, Assets &res, int &errCode)
43 {
44 if (isNormalStatus) {
45 TagSingleAsset(flag, AssetStatus::NORMAL, asset, res, errCode);
46 return;
47 }
48 TagSingleAsset(flag, AssetStatus::DOWNLOADING, asset, res, errCode);
49 }
50
TagAssetsWithNormalStatus(const bool isNormalStatus,AssetOpType flag,Assets & assets,Assets & res,int & errCode)51 void TagAssetsWithNormalStatus(const bool isNormalStatus, AssetOpType flag,
52 Assets &assets, Assets &res, int &errCode)
53 {
54 for (Asset &asset : assets) {
55 TagAssetWithNormalStatus(isNormalStatus, flag, asset, res, errCode);
56 if (errCode != E_OK) {
57 break;
58 }
59 }
60 }
61
62 template<typename T>
IsDataContainField(const std::string & assetFieldName,const VBucket & data)63 bool IsDataContainField(const std::string &assetFieldName, const VBucket &data)
64 {
65 auto assetIter = data.find(assetFieldName);
66 if (assetIter == data.end()) {
67 return false;
68 }
69 // When type of Assets is not Nil but a vector which size is 0, we think data is not contain this field.
70 if (assetIter->second.index() == TYPE_INDEX<Assets>) {
71 if (std::get<Assets>(assetIter->second).empty()) {
72 return false;
73 }
74 }
75 if (assetIter->second.index() != TYPE_INDEX<T>) {
76 return false;
77 }
78 return true;
79 }
80
81 // AssetOpType and AssetStatus will be tagged, assets to be changed will be returned
82 // use VBucket rather than Type because we need to check whether it is empty
TagAssets(const std::string & assetFieldName,VBucket & coveredData,VBucket & beCoveredData,bool setNormalStatus,int & errCode)83 Assets TagAssets(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData,
84 bool setNormalStatus, int &errCode)
85 {
86 Assets res = {};
87 bool beCoveredHasAssets = IsDataContainField<Assets>(assetFieldName, beCoveredData);
88 bool coveredHasAssets = IsDataContainField<Assets>(assetFieldName, coveredData);
89 if (!beCoveredHasAssets) {
90 if (!coveredHasAssets) {
91 return res;
92 }
93 // all the element in assets will be set to INSERT
94 TagAssetsWithNormalStatus(setNormalStatus,
95 AssetOpType::INSERT, std::get<Assets>(coveredData[assetFieldName]), res, errCode);
96 return res;
97 }
98 if (!coveredHasAssets) {
99 // all the element in assets will be set to DELETE
100 TagAssetsWithNormalStatus(setNormalStatus,
101 AssetOpType::DELETE, std::get<Assets>(beCoveredData[assetFieldName]), res, errCode);
102 coveredData[assetFieldName] = res;
103 return res;
104 }
105 Assets &covered = std::get<Assets>(coveredData[assetFieldName]);
106 Assets &beCovered = std::get<Assets>(beCoveredData[assetFieldName]);
107 std::map<std::string, size_t> coveredAssetsIndexMap = CloudStorageUtils::GenAssetsIndexMap(covered);
108 for (Asset &beCoveredAsset : beCovered) {
109 auto it = coveredAssetsIndexMap.find(beCoveredAsset.name);
110 if (it == coveredAssetsIndexMap.end()) {
111 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::DELETE, beCoveredAsset, res, errCode);
112 std::get<Assets>(coveredData[assetFieldName]).push_back(beCoveredAsset);
113 continue;
114 }
115 Asset &coveredAsset = covered[it->second];
116 if (beCoveredAsset.hash != coveredAsset.hash) {
117 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::UPDATE, coveredAsset, res, errCode);
118 } else {
119 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::NO_CHANGE, coveredAsset, res, errCode);
120 }
121 // Erase element which has been handled, remaining element will be set to Insert
122 coveredAssetsIndexMap.erase(it);
123 if (errCode != E_OK) {
124 LOGE("Tag assets UPDATE or NO_CHANGE fail!");
125 break;
126 }
127 }
128 for (const auto &noHandledAssetKvPair : coveredAssetsIndexMap) {
129 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::INSERT,
130 covered[noHandledAssetKvPair.second], res, errCode);
131 if (errCode != E_OK) {
132 LOGE("Tag assets INSERT fail!");
133 break;
134 }
135 }
136 return res;
137 }
138
139 // AssetOpType and AssetStatus will be tagged, assets to be changed will be returned
TagAsset(const std::string & assetFieldName,VBucket & coveredData,VBucket & beCoveredData,bool setNormalStatus,int & errCode)140 Assets TagAsset(const std::string &assetFieldName, VBucket &coveredData, VBucket &beCoveredData,
141 bool setNormalStatus, int &errCode)
142 {
143 Assets res = {};
144 bool beCoveredHasAsset = IsDataContainField<Asset>(assetFieldName, beCoveredData) ||
145 IsDataContainField<Assets>(assetFieldName, beCoveredData);
146 bool coveredHasAsset = IsDataContainField<Asset>(assetFieldName, coveredData);
147 if (!beCoveredHasAsset) {
148 if (!coveredHasAsset) {
149 LOGD("[CloudSyncer] Both data do not contain certain asset field");
150 return res;
151 }
152 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::INSERT,
153 std::get<Asset>(coveredData[assetFieldName]), res, errCode);
154 return res;
155 }
156 if (!coveredHasAsset) {
157 if (beCoveredData[assetFieldName].index() == TYPE_INDEX<Asset>) {
158 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::DELETE,
159 std::get<Asset>(beCoveredData[assetFieldName]), res, errCode);
160 } else if (beCoveredData[assetFieldName].index() == TYPE_INDEX<Assets>) {
161 TagAssetsWithNormalStatus(setNormalStatus, AssetOpType::DELETE,
162 std::get<Assets>(beCoveredData[assetFieldName]), res, errCode);
163 }
164 return res;
165 }
166 Asset &covered = std::get<Asset>(coveredData[assetFieldName]);
167 Asset beCovered;
168 if (beCoveredData[assetFieldName].index() == TYPE_INDEX<Asset>) {
169 // This indicates that asset in cloudData is stored as Asset
170 beCovered = std::get<Asset>(beCoveredData[assetFieldName]);
171 } else if (beCoveredData[assetFieldName].index() == TYPE_INDEX<Assets>) {
172 // Stored as ASSETS, first element in assets will be the target asset
173 beCovered = (std::get<Assets>(beCoveredData[assetFieldName]))[0];
174 } else {
175 LOGE("The type of data is neither Asset nor Assets");
176 return res;
177 }
178 if (covered.name != beCovered.name) {
179 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::INSERT, covered, res, errCode);
180 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::DELETE, beCovered, res, errCode);
181 return res;
182 }
183 if (covered.hash != beCovered.hash) {
184 TagAssetWithNormalStatus(setNormalStatus, AssetOpType::UPDATE, covered, res, errCode);
185 } else {
186 Assets tmpAssets;
187 TagAssetWithNormalStatus(true, AssetOpType::NO_CHANGE, covered, tmpAssets, errCode);
188 }
189 return res;
190 }
191 } // namespace
192
TagAssetsInSingleCol(VBucket & coveredData,VBucket & beCoveredData,const Field & assetField,bool setNormalStatus,int & errCode)193 Assets TagAssetsInSingleCol(
194 VBucket &coveredData, VBucket &beCoveredData, const Field &assetField, bool setNormalStatus, int &errCode)
195 {
196 // Define a list to store the tagged result
197 Assets assets = {};
198 switch (assetField.type) {
199 case TYPE_INDEX<Assets>: {
200 assets = TagAssets(assetField.colName, coveredData, beCoveredData, setNormalStatus, errCode);
201 break;
202 }
203 case TYPE_INDEX<Asset>: {
204 assets = TagAsset(assetField.colName, coveredData, beCoveredData, setNormalStatus, errCode);
205 break;
206 }
207 default:
208 LOGW("[CloudSyncer] Meet an unexpected type %d", assetField.type);
209 break;
210 }
211 return assets;
212 }
213 } // namespace DistributedDB
214