1 /*
2 * Copyright (C) 2025 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 "clone_restore_analysis_data.h"
16
17 #include "backup_database_utils.h"
18 #include "media_backup_report_data_type.h"
19 #include "media_file_utils.h"
20 #include "media_log.h"
21 #include "result_set_utils.h"
22 #include "upgrade_restore_task_report.h"
23
24 namespace OHOS::Media {
25 const int32_t PAGE_SIZE = 200;
26 const std::string FILE_ID = "file_id";
27 const int32_t ANALYSIS_STATUS_SUCCESS = 1;
28 static const uint8_t BINARY_FEATURE_END_FLAG = 0x01;
29 const unordered_map<string, ResultSetDataType> COLUMN_TYPE_MAP = {
30 { "INT", ResultSetDataType::TYPE_INT32 },
31 { "INTEGER", ResultSetDataType::TYPE_INT32 },
32 { "BIGINT", ResultSetDataType::TYPE_INT64 },
33 { "DOUBLE", ResultSetDataType::TYPE_DOUBLE },
34 { "REAL", ResultSetDataType::TYPE_DOUBLE },
35 { "TEXT", ResultSetDataType::TYPE_STRING },
36 { "BLOB", ResultSetDataType::TYPE_BLOB },
37 };
38
Init(int32_t sceneCode,const std::string & taskId,std::shared_ptr<NativeRdb::RdbStore> mediaRdb,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)39 void CloneRestoreAnalysisData::Init(int32_t sceneCode, const std::string &taskId,
40 std::shared_ptr<NativeRdb::RdbStore> mediaRdb,
41 std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
42 {
43 sceneCode_ = sceneCode;
44 taskId_ = taskId;
45 mediaRdb_ = mediaRdb;
46 mediaLibraryRdb_ = mediaLibraryRdb;
47 }
48
GetTableCommonColumns(const std::unordered_set<std::string> & excludedColumns)49 std::unordered_map<std::string, std::string> CloneRestoreAnalysisData::GetTableCommonColumns(
50 const std::unordered_set<std::string> &excludedColumns)
51 {
52 std::unordered_map<std::string, std::string> srcColumnInfoMap =
53 BackupDatabaseUtils::GetColumnInfoMap(mediaRdb_, table_);
54 std::unordered_map<std::string, std::string> dstColumnInfoMap =
55 BackupDatabaseUtils::GetColumnInfoMap(mediaLibraryRdb_, table_);
56 std::unordered_map<std::string, std::string> result;
57 for (auto it = dstColumnInfoMap.begin(); it != dstColumnInfoMap.end(); ++it) {
58 bool cond = (srcColumnInfoMap.find(it->first) != srcColumnInfoMap.end() &&
59 excludedColumns.find(it->first) == excludedColumns.end());
60 CHECK_AND_EXECUTE(!cond, result[it->first] = it->second);
61 }
62 return result;
63 }
64
65
66 template<typename Key, typename Value>
GetValueFromMap(const unordered_map<Key,Value> & map,const Key & key,const Value & defaultValue=Value ())67 Value GetValueFromMap(const unordered_map<Key, Value> &map, const Key &key, const Value &defaultValue = Value())
68 {
69 auto it = map.find(key);
70 CHECK_AND_RETURN_RET(it != map.end(), defaultValue);
71 return it->second;
72 }
73
GetValFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,AnalysisDataInfo & info,const std::string & columnName,const std::string & columnType)74 void CloneRestoreAnalysisData::GetValFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> &resultSet,
75 AnalysisDataInfo &info, const std::string &columnName, const std::string &columnType)
76 {
77 int32_t columnIndex = 0;
78 int32_t errCode = resultSet->GetColumnIndex(columnName, columnIndex);
79 CHECK_AND_RETURN_LOG(errCode == 0, "Get column index errCode: %{public}d", errCode);
80 bool isNull = false;
81 errCode = resultSet->IsColumnNull(columnIndex, isNull);
82 if (errCode || isNull) {
83 return;
84 }
85 ResultSetDataType dataType = GetValueFromMap(COLUMN_TYPE_MAP, columnType, ResultSetDataType::TYPE_NULL);
86 switch (dataType) {
87 case ResultSetDataType::TYPE_INT32: {
88 int32_t int32Val;
89 if (resultSet->GetInt(columnIndex, int32Val) == E_OK) {
90 info.columnValMap[columnName] = int32Val;
91 }
92 break;
93 }
94 case ResultSetDataType::TYPE_INT64: {
95 int64_t int64Val;
96 if (resultSet->GetLong(columnIndex, int64Val) == E_OK) {
97 info.columnValMap[columnName] = int64Val;
98 }
99 break;
100 }
101 case ResultSetDataType::TYPE_DOUBLE: {
102 double doubleVal;
103 if (resultSet->GetDouble(columnIndex, doubleVal) == E_OK) {
104 info.columnValMap[columnName] = doubleVal;
105 }
106 break;
107 }
108 case ResultSetDataType::TYPE_BLOB: {
109 std::vector<uint8_t> blobVal;
110 if (resultSet->GetBlob(columnIndex, blobVal) == E_OK) {
111 info.columnValMap[columnName] = blobVal;
112 }
113 break;
114 }
115 case ResultSetDataType::TYPE_STRING: {
116 std::vector<uint8_t> blobVal;
117 if (resultSet->GetBlob(columnIndex, blobVal) == E_OK) {
118 if (!blobVal.empty() && blobVal.back() == BINARY_FEATURE_END_FLAG) {
119 info.columnValMap[columnName] = blobVal;
120 } else {
121 std::string strVal;
122 resultSet->GetString(columnIndex, strVal);
123 info.columnValMap[columnName] = strVal;
124 }
125 }
126 break;
127 }
128 default:
129 MEDIA_ERR_LOG("No such column type: %{public}s", columnType.c_str());
130 }
131 }
132
GetAnalysisDataRowInfo(AnalysisDataInfo & info,const std::shared_ptr<NativeRdb::ResultSet> & resultSet)133 void CloneRestoreAnalysisData::GetAnalysisDataRowInfo(AnalysisDataInfo &info,
134 const std::shared_ptr<NativeRdb::ResultSet> &resultSet)
135 {
136 info.fileId = GetInt32Val(FILE_ID, resultSet);
137 for (auto it = tableCommonColumns_.begin(); it != tableCommonColumns_.end(); ++it) {
138 std::string columnName = it->first;
139 std::string columnType = it->second;
140 GetValFromResultSet(resultSet, info, columnName, columnType);
141 }
142 }
143
GetAnalysisDataInfo()144 void CloneRestoreAnalysisData::GetAnalysisDataInfo()
145 {
146 CHECK_AND_RETURN_LOG(mediaRdb_ != nullptr, "rdbStore is nullptr");
147 std::stringstream querySql;
148 std::string placeHolders;
149 std::vector<NativeRdb::ValueObject> params;
150 cloneRestoreAnalysisTotal_.SetPlaceHoldersAndParamsByFileIdOld(placeHolders, params);
151 querySql << "SELECT * FROM " + table_ + " WHERE " + FILE_ID + " IN (" << placeHolders << ")";
152 auto resultSet = BackupDatabaseUtils::QuerySql(mediaRdb_, querySql.str(), params);
153 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Query resultSql is null.");
154 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
155 AnalysisDataInfo info;
156 GetAnalysisDataRowInfo(info, resultSet);
157 analysisDataInfos_.emplace_back(info);
158 }
159 resultSet->Close();
160 MEDIA_INFO_LOG("query %{public}s nums: %{public}zu", table_.c_str(), analysisDataInfos_.size());
161 }
162
PrepareCommonColumnVal(NativeRdb::ValuesBucket & value,const std::string & columnName,const std::string & columnType,const std::variant<int32_t,int64_t,double,std::string,std::vector<uint8_t>> & columnVal)163 void CloneRestoreAnalysisData::PrepareCommonColumnVal(NativeRdb::ValuesBucket &value, const std::string &columnName,
164 const std::string &columnType,
165 const std::variant<int32_t, int64_t, double, std::string, std::vector<uint8_t>> &columnVal)
166 {
167 ResultSetDataType dataType = GetValueFromMap(COLUMN_TYPE_MAP, columnType, ResultSetDataType::TYPE_NULL);
168 switch (dataType) {
169 case ResultSetDataType::TYPE_INT32: {
170 value.PutInt(columnName, get<int32_t>(columnVal));
171 break;
172 }
173 case ResultSetDataType::TYPE_INT64: {
174 value.PutLong(columnName, get<int64_t>(columnVal));
175 break;
176 }
177 case ResultSetDataType::TYPE_DOUBLE: {
178 value.PutDouble(columnName, get<double>(columnVal));
179 break;
180 }
181 case ResultSetDataType::TYPE_BLOB: {
182 value.PutBlob(columnName, get<std::vector<uint8_t>>(columnVal));
183 break;
184 }
185 case ResultSetDataType::TYPE_STRING: {
186 if (std::holds_alternative<std::string>(columnVal)) {
187 value.PutString(columnName, get<std::string>(columnVal));
188 } else {
189 value.PutBlob(columnName, get<std::vector<uint8_t>>(columnVal));
190 }
191 break;
192 }
193 default:
194 MEDIA_ERR_LOG("No such column type: %{public}s", columnType.c_str());
195 }
196 }
197
GetAnalysisDataInsertValue(NativeRdb::ValuesBucket & value,const AnalysisDataInfo & info)198 void CloneRestoreAnalysisData::GetAnalysisDataInsertValue(NativeRdb::ValuesBucket &value,
199 const AnalysisDataInfo &info)
200 {
201 for (auto it = tableCommonColumns_.begin(); it != tableCommonColumns_.end(); ++it) {
202 std::string columnName = it->first;
203 std::string columnType = it->second;
204 auto columnIndex = info.columnValMap.find(columnName);
205 if (columnIndex != info.columnValMap.end()) {
206 PrepareCommonColumnVal(value, columnName, columnType, columnIndex->second);
207 }
208 }
209 value.PutInt(MEDIA_DATA_DB_ID, info.fileId);
210 }
211
BatchInsertWithRetry(const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & values,int64_t & rowNum)212 int32_t CloneRestoreAnalysisData::BatchInsertWithRetry(const std::string &tableName,
213 std::vector<NativeRdb::ValuesBucket> &values, int64_t &rowNum)
214 {
215 if (values.empty()) {
216 return 0;
217 }
218
219 int32_t errCode = E_ERR;
220 TransactionOperations trans{ __func__ };
221 trans.SetBackupRdbStore(mediaLibraryRdb_);
222 std::function<int(void)> func = [&]()->int {
223 errCode = trans.BatchInsert(rowNum, tableName, values);
224 CHECK_AND_PRINT_LOG(errCode == E_OK,
225 "InsertSql failed, errCode: %{public}d, rowNum: %{public}ld.", errCode, (long)rowNum);
226 return errCode;
227 };
228 errCode = trans.RetryTrans(func, true);
229 CHECK_AND_PRINT_LOG(errCode == E_OK, "BatchInsertWithRetry: tans finish fail!, ret:%{public}d", errCode);
230 return errCode;
231 }
232
RemoveDuplicateInfos(const std::unordered_set<int32_t> & existingFileIds)233 void CloneRestoreAnalysisData::RemoveDuplicateInfos(const std::unordered_set<int32_t> &existingFileIds)
234 {
235 analysisDataInfos_.erase(std::remove_if(analysisDataInfos_.begin(), analysisDataInfos_.end(),
236 [&](AnalysisDataInfo &info) {
237 size_t index = cloneRestoreAnalysisTotal_.FindIndexByFileIdOld(info.fileId);
238 if (index == std::string::npos) {
239 return true;
240 }
241
242 int32_t fileIdNew = cloneRestoreAnalysisTotal_.GetFileIdNewByIndex(index);
243 info.fileId = fileIdNew;
244 if (existingFileIds.count(fileIdNew) == 0) {
245 return false;
246 }
247 cloneRestoreAnalysisTotal_.UpdateRestoreStatusAsDuplicateByIndex(index);
248 duplicateCnt_++;
249 return true;
250 }),
251 analysisDataInfos_.end());
252 }
253
GetExistingFileIds()254 std::unordered_set<int32_t> CloneRestoreAnalysisData::GetExistingFileIds()
255 {
256 std::unordered_set<int32_t> existingFileIds;
257 std::stringstream querySql;
258 std::string placeHolders;
259 std::vector<NativeRdb::ValueObject> params;
260 cloneRestoreAnalysisTotal_.SetPlaceHoldersAndParamsByFileIdNew(placeHolders, params);
261 querySql << "SELECT file_id FROM " + table_ + " WHERE " + FILE_ID + " IN (" << placeHolders << ")";
262 auto resultSet = BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, querySql.str(), params);
263 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, existingFileIds, "Query resultSql is null.");
264 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
265 int32_t fileId = GetInt32Val(FILE_ID, resultSet);
266 existingFileIds.insert(fileId);
267 }
268 resultSet->Close();
269 return existingFileIds;
270 }
271
DeleteDuplicateInfos()272 void CloneRestoreAnalysisData::DeleteDuplicateInfos()
273 {
274 CHECK_AND_RETURN(!analysisDataInfos_.empty());
275 std::unordered_set<int32_t> existingFileIds = GetExistingFileIds();
276 RemoveDuplicateInfos(existingFileIds);
277 }
278
InsertIntoAnalysisTable()279 void CloneRestoreAnalysisData::InsertIntoAnalysisTable()
280 {
281 DeleteDuplicateInfos();
282 CHECK_AND_RETURN(!analysisDataInfos_.empty());
283 size_t offset = 0;
284 do {
285 std::vector<NativeRdb::ValuesBucket> values;
286 for (size_t index = 0; index < PAGE_SIZE && index + offset < analysisDataInfos_.size(); index++) {
287 NativeRdb::ValuesBucket value;
288 GetAnalysisDataInsertValue(value, analysisDataInfos_[index + offset]);
289 values.emplace_back(value);
290 }
291 int64_t rowNum = 0;
292 int32_t errCode = BatchInsertWithRetry(table_, values, rowNum);
293 if (errCode != E_OK || rowNum != static_cast<int64_t>(values.size())) {
294 int64_t failNums = static_cast<int64_t>(values.size()) - rowNum;
295 MEDIA_ERR_LOG("insert into %{public}s fail, num: %{public}" PRId64, table_.c_str(), failNums);
296 ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode),
297 "insert into " + table_ + "fail, num:" + std::to_string(failNums));
298 failCnt_ += failNums;
299 cloneRestoreAnalysisTotal_.UpdateRestoreStatusAsFailed();
300 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
301 }
302 offset += PAGE_SIZE;
303 successCnt_ += rowNum;
304 } while (offset < analysisDataInfos_.size());
305 }
306
RestoreAnalysisDataMaps()307 void CloneRestoreAnalysisData::RestoreAnalysisDataMaps()
308 {
309 bool cond = (mediaRdb_ == nullptr || mediaLibraryRdb_ == nullptr);
310 CHECK_AND_RETURN_LOG(!cond, "rdbStore is nullptr");
311
312 int64_t start = MediaFileUtils::UTCTimeMilliSeconds();
313 analysisDataInfos_.clear();
314 GetAnalysisDataInfo();
315 int64_t startInsert = MediaFileUtils::UTCTimeMilliSeconds();
316 InsertIntoAnalysisTable();
317 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
318 MEDIA_INFO_LOG("TimeCost: Clone analysis table: %{public}s,"
319 "GetInfos: %{public}" PRId64 ", InsertIntoTable: %{public}" PRId64,
320 table_.c_str(), startInsert - start, end - startInsert);
321 }
322
AnalysisDataRestoreBatch()323 void CloneRestoreAnalysisData::AnalysisDataRestoreBatch()
324 {
325 int64_t startGet = MediaFileUtils::UTCTimeMilliSeconds();
326 cloneRestoreAnalysisTotal_.GetInfos(photoInfoMap_);
327 int64_t startRestoreMaps = MediaFileUtils::UTCTimeMilliSeconds();
328 RestoreAnalysisDataMaps();
329 int64_t startUpdate = MediaFileUtils::UTCTimeMilliSeconds();
330 cloneRestoreAnalysisTotal_.UpdateDatabase();
331 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
332 MEDIA_INFO_LOG("TimeCost: Clone analysis table: %{public}s, GetAnalysisTotalInfos: %{public}" PRId64
333 ", RestoreMaps: %{public}" PRId64", UpdateDatabase: %{public}" PRId64,
334 table_.c_str(), startRestoreMaps - startGet, startUpdate - startRestoreMaps, end - startUpdate);
335 }
336
ReportRestoreTaskOfTotal()337 void CloneRestoreAnalysisData::ReportRestoreTaskOfTotal()
338 {
339 RestoreTaskInfo info;
340 cloneRestoreAnalysisTotal_.SetRestoreTaskInfo(info);
341 info.type = "CLONE_RESTORE_" + ToUpper(analysisType_) +"_TOTAL";
342 info.errorCode = std::to_string(ANALYSIS_STATUS_SUCCESS);
343 info.errorInfo = "timeCost: " + std::to_string(restoreTimeCost_);
344 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).Report(info);
345 }
346
ReportRestoreTaskofData()347 void CloneRestoreAnalysisData::ReportRestoreTaskofData()
348 {
349 RestoreTaskInfo info;
350 info.type = "CLONE_RESTORE_" + ToUpper(analysisType_) +"_DATA";
351 info.errorCode = std::to_string(ANALYSIS_STATUS_SUCCESS);
352 info.errorInfo = "max_id: " + std::to_string(maxId_);
353 info.successCount = successCnt_;
354 info.failedCount = failCnt_;
355 info.duplicateCount = duplicateCnt_;
356 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).Report(info);
357 }
358
ReportAnalysisTableRestoreTask()359 void CloneRestoreAnalysisData::ReportAnalysisTableRestoreTask()
360 {
361 ReportRestoreTaskOfTotal();
362 ReportRestoreTaskofData();
363 }
364
GetMaxIds()365 void CloneRestoreAnalysisData::GetMaxIds()
366 {
367 maxId_ = BackupDatabaseUtils::QueryMaxId(mediaLibraryRdb_, table_, "rowid");
368 }
369
CloneAnalysisData(const std::string & table,const std::string & type,const std::unordered_map<int32_t,PhotoInfo> & photoInfoMap,const std::unordered_set<std::string> & excludedColumns)370 void CloneRestoreAnalysisData::CloneAnalysisData(const std::string &table, const std::string &type,
371 const std::unordered_map<int32_t, PhotoInfo> &photoInfoMap, const std::unordered_set<std::string> &excludedColumns)
372 {
373 totalCnt_ = 0;
374 successCnt_ = 0;
375 failCnt_ = 0;
376 duplicateCnt_ = 0;
377 table_ = table;
378 analysisType_ = type;
379 photoInfoMap_ = photoInfoMap;
380
381 CloneRestoreAnalysisTotal cloneRestoreAnalysisTotal;
382 cloneRestoreAnalysisTotal.Init(analysisType_, PAGE_SIZE, mediaRdb_, mediaLibraryRdb_);
383 cloneRestoreAnalysisTotal_ = cloneRestoreAnalysisTotal;
384
385 int64_t start = MediaFileUtils::UTCTimeMilliSeconds();
386 tableCommonColumns_ = GetTableCommonColumns(excludedColumns);
387 int32_t totalNumber = cloneRestoreAnalysisTotal_.GetTotalNumber();
388 for (int32_t offset = 0; offset < totalNumber; offset += PAGE_SIZE) {
389 AnalysisDataRestoreBatch();
390 }
391 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
392 restoreTimeCost_ = end - start;
393 MEDIA_INFO_LOG("TimeCost: clone analysis table :%{public}s, number:%{public}d, cost time: %{public}" PRId64,
394 table_.c_str(), totalNumber, end - start);
395 ReportAnalysisTableRestoreTask();
396 }
397 }