• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }