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 #define MLOG_TAG "CloneRestoreGeo"
16
17 #include "clone_restore_geo.h"
18
19 #include "backup_database_utils.h"
20 #include "locale_config.h"
21 #include "medialibrary_unistore_manager.h"
22 #include "media_file_utils.h"
23 #include "media_log.h"
24 #include "result_set_utils.h"
25 #include "upgrade_restore_task_report.h"
26
27 namespace OHOS::Media {
28 const int32_t PAGE_SIZE = 200;
29
30 const string LATITUDE = "latitude";
31 const string LONGITUDE = "longitude";
32 const string LOCATION_KEY = "location_key";
33 const string CITY_ID = "city_id";
34 const string LANGUAGE = "language";
35 const string COUNTRY = "country";
36 const string ADMIN_AREA = "admin_area";
37 const string SUB_ADMIN_AREA = "sub_admin_area";
38 const string LOCALITY = "locality";
39 const string SUB_LOCALITY = "sub_locality";
40 const string THOROUGHFARE = "thoroughfare";
41 const string SUB_THOROUGHFARE = "sub_thoroughfare";
42 const string FEATURE_NAME = "feature_name";
43 const string CITY_NAME = "city_name";
44 const string ADDRESS_DESCRIPTION = "address_description";
45 const string AOI = "aoi";
46 const string POI = "poi";
47 const string FIRST_AOI = "first_aoi";
48 const string FIRST_POI = "first_poi";
49 const string LOCATION_VERSION = "location_version";
50 const string FIRST_AOI_CATEGORY = "first_aoi_category";
51 const string FIRST_POI_CATEGORY = "first_poi_category";
52 const string LOCATION_TYPE = "location_type";
53 const string FILE_ID = "file_id";
54
55 const string GEO = "geo";
56 const string GEO_KNOWLEDGE_TABLE = "tab_analysis_geo_knowledge";
57 const string INTEGER = "INTEGER";
58 const int32_t GEO_STATUS_SUCCESS = 1;
59
60 const unordered_map<string, unordered_set<string>> COMPARED_COLUMNS_MAP = {
61 { "tab_analysis_geo_knowledge",
62 {
63 "latitude",
64 "longitude",
65 "location_key",
66 "city_id",
67 "language",
68 "country",
69 "admin_area",
70 "sub_admin_area",
71 "locality",
72 "sub_locality",
73 "thoroughfare",
74 "sub_thoroughfare",
75 "feature_name",
76 "city_name",
77 "address_description",
78 "aoi",
79 "poi",
80 "first_aoi",
81 "first_poi",
82 "location_version",
83 "first_aoi_category",
84 "first_poi_category",
85 "file_id",
86 "location_type"
87 }
88 }
89 };
90
91 template<typename Key, typename Value>
GetValueFromMap(const unordered_map<Key,Value> & map,const Key & key,const Value & defaultValue=Value ())92 Value GetValueFromMap(const unordered_map<Key, Value> &map, const Key &key, const Value &defaultValue = Value())
93 {
94 auto it = map.find(key);
95 CHECK_AND_RETURN_RET(it != map.end(), defaultValue);
96 return it->second;
97 }
98
Init(int32_t sceneCode,const std::string & taskId,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,std::shared_ptr<NativeRdb::RdbStore> mediaRdb)99 void CloneRestoreGeo::Init(int32_t sceneCode, const std::string &taskId,
100 std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb, std::shared_ptr<NativeRdb::RdbStore> mediaRdb)
101 {
102 sceneCode_ = sceneCode;
103 taskId_ = taskId;
104 mediaLibraryRdb_ = mediaLibraryRdb;
105 mediaRdb_ = mediaRdb;
106 systemLanguage_ = Global::I18n::LocaleConfig::GetSystemLanguage();
107 analysisType_ = "geo";
108 }
109
Restore(const std::unordered_map<int32_t,PhotoInfo> & photoInfoMap)110 void CloneRestoreGeo::Restore(const std::unordered_map<int32_t, PhotoInfo> &photoInfoMap)
111 {
112 CHECK_AND_RETURN_LOG(mediaRdb_ != nullptr && mediaLibraryRdb_ != nullptr,
113 "Restore failed, rdbStore is nullptr");
114
115 int64_t start = MediaFileUtils::UTCTimeMilliSeconds();
116 GetMaxIds();
117 cloneRestoreAnalysisTotal_.Init(analysisType_, PAGE_SIZE, mediaRdb_, mediaLibraryRdb_);
118 int32_t totalNumber = cloneRestoreAnalysisTotal_.GetTotalNumber();
119 for (int32_t offset = 0; offset < totalNumber; offset += PAGE_SIZE) {
120 RestoreBatch(photoInfoMap);
121 }
122 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
123 restoreTimeCost_ = end - start;
124 ReportRestoreTask();
125 MEDIA_INFO_LOG("TimeCost: Restore: %{public}" PRId64, end - start);
126 }
127
GetMaxIds()128 void CloneRestoreGeo::GetMaxIds()
129 {
130 maxId_ = BackupDatabaseUtils::QueryMaxId(mediaLibraryRdb_, GEO_KNOWLEDGE_TABLE, "rowid");
131 }
132
RestoreBatch(const std::unordered_map<int32_t,PhotoInfo> & photoInfoMap)133 void CloneRestoreGeo::RestoreBatch(const std::unordered_map<int32_t, PhotoInfo> &photoInfoMap)
134 {
135 int64_t startGet = MediaFileUtils::UTCTimeMilliSeconds();
136 cloneRestoreAnalysisTotal_.GetInfos(photoInfoMap);
137 int64_t startRestoreMaps = MediaFileUtils::UTCTimeMilliSeconds();
138 RestoreMaps();
139 int64_t startUpdate = MediaFileUtils::UTCTimeMilliSeconds();
140 cloneRestoreAnalysisTotal_.UpdateDatabase();
141 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
142 MEDIA_INFO_LOG("TimeCost: GetAnalysisTotalInfos: %{public}" PRId64 ", RestoreMaps: %{public}" PRId64
143 ", UpdateDatabase: %{public}" PRId64,
144 startRestoreMaps - startGet, startUpdate - startRestoreMaps, end - startUpdate);
145 }
146
RestoreMaps()147 void CloneRestoreGeo::RestoreMaps()
148 {
149 bool cond = (mediaRdb_ == nullptr || mediaLibraryRdb_ == nullptr);
150 CHECK_AND_RETURN_LOG(!cond, "rdbStore is nullptr");
151
152 MEDIA_INFO_LOG("restore geo knowledge start.");
153 int64_t start = MediaFileUtils::UTCTimeMilliSeconds();
154 std::vector<GeoCloneInfo> infos;
155 GetInfos(infos);
156 int64_t startInsert = MediaFileUtils::UTCTimeMilliSeconds();
157 InsertIntoTable(infos);
158 int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
159 MEDIA_INFO_LOG("TimeCost: GetInfos: %{public}" PRId64 ", InsertIntoTable: %{public}" PRId64,
160 startInsert - start, end - startInsert);
161 MEDIA_INFO_LOG("restore geo knowledge end.");
162 }
163
GetInfos(std::vector<GeoCloneInfo> & infos)164 void CloneRestoreGeo::GetInfos(std::vector<GeoCloneInfo> &infos)
165 {
166 std::unordered_map<std::string, std::string> columns;
167 columns[FILE_ID] = INTEGER;
168 bool hasRequiredColumns = CheckTableColumns(GEO_KNOWLEDGE_TABLE, columns);
169 if (!hasRequiredColumns) {
170 MEDIA_ERR_LOG("The tab_analysis_geo_knowledge does not contain the required columns.");
171 ErrorInfo errorInfo(RestoreError::TABLE_LACK_OF_COLUMN, static_cast<int32_t>(columns.size()),
172 "", "The tab_analysis_geo_knowledge does not contain file_id");
173 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
174 return;
175 }
176
177 std::stringstream querySql;
178 std::string placeHolders;
179 std::vector<NativeRdb::ValueObject> params;
180 cloneRestoreAnalysisTotal_.SetPlaceHoldersAndParamsByFileIdOld(placeHolders, params);
181 querySql << "SELECT * FROM " + GEO_KNOWLEDGE_TABLE + " WHERE " + FILE_ID + " IN (" << placeHolders << ")";
182
183 auto resultSet = BackupDatabaseUtils::QuerySql(mediaRdb_, querySql.str(), params);
184 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Query resultSql is null.");
185 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
186 GeoCloneInfo info;
187 GetInfo(info, resultSet);
188 infos.emplace_back(info);
189 }
190 resultSet->Close();
191 MEDIA_INFO_LOG("query tab_analysis_geo_knowledge nums: %{public}zu", infos.size());
192 }
193
DeduplicateInfos(std::vector<GeoCloneInfo> & infos)194 void CloneRestoreGeo::DeduplicateInfos(std::vector<GeoCloneInfo> &infos)
195 {
196 CHECK_AND_RETURN(!infos.empty());
197 std::unordered_set<int32_t> existingFileIds = GetExistingFileIds(GEO_KNOWLEDGE_TABLE);
198 RemoveDuplicateInfos(infos, existingFileIds);
199 MEDIA_INFO_LOG("existing: %{public}zu, after deduplicate: %{public}zu", existingFileIds.size(), infos.size());
200 }
201
GetExistingFileIds(const std::string & tableName)202 std::unordered_set<int32_t> CloneRestoreGeo::GetExistingFileIds(const std::string &tableName)
203 {
204 std::unordered_set<int32_t> existingFileIds;
205 std::stringstream querySql;
206 std::string placeHolders;
207 std::vector<NativeRdb::ValueObject> params;
208 cloneRestoreAnalysisTotal_.SetPlaceHoldersAndParamsByFileIdNew(placeHolders, params);
209 querySql << "SELECT file_id FROM " + tableName + " WHERE " + FILE_ID + " IN (" << placeHolders << ")";
210
211 auto resultSet = BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, querySql.str(), params);
212 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, existingFileIds, "Query resultSql is null.");
213 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
214 int32_t fileId = GetInt32Val("file_id", resultSet);
215 existingFileIds.insert(fileId);
216 }
217 resultSet->Close();
218 return existingFileIds;
219 }
220
RemoveDuplicateInfos(std::vector<GeoCloneInfo> & infos,const std::unordered_set<int32_t> & existingFileIds)221 void CloneRestoreGeo::RemoveDuplicateInfos(std::vector<GeoCloneInfo> &infos,
222 const std::unordered_set<int32_t> &existingFileIds)
223 {
224 infos.erase(std::remove_if(infos.begin(), infos.end(), [&](GeoCloneInfo &info) {
225 if (!info.fileIdOld.has_value()) {
226 return true;
227 }
228
229 size_t index = cloneRestoreAnalysisTotal_.FindIndexByFileIdOld(info.fileIdOld.value());
230 if (index == std::string::npos) {
231 return true;
232 }
233
234 int32_t fileIdNew = cloneRestoreAnalysisTotal_.GetFileIdNewByIndex(index);
235 info.fileIdNew = fileIdNew;
236 if (existingFileIds.count(fileIdNew) == 0) {
237 return false;
238 }
239 cloneRestoreAnalysisTotal_.UpdateRestoreStatusAsDuplicateByIndex(index);
240 duplicateCnt_++;
241 return true;
242 }),
243 infos.end());
244 }
245
InsertIntoTable(std::vector<GeoCloneInfo> & infos)246 void CloneRestoreGeo::InsertIntoTable(std::vector<GeoCloneInfo> &infos)
247 {
248 DeduplicateInfos(infos);
249 CHECK_AND_RETURN(!infos.empty());
250
251 std::unordered_set<std::string> intersection = GetCommonColumns(GEO_KNOWLEDGE_TABLE);
252 int64_t startInsert = MediaFileUtils::UTCTimeMilliSeconds();
253 size_t offset = 0;
254 do {
255 std::vector<NativeRdb::ValuesBucket> values;
256 for (size_t index = 0; index < PAGE_SIZE && index + offset < infos.size(); index++) {
257 CHECK_AND_CONTINUE(infos[index + offset].fileIdNew.has_value());
258 NativeRdb::ValuesBucket value;
259 GetMapInsertValue(value, infos[index + offset], intersection);
260 values.emplace_back(value);
261 }
262 MEDIA_INFO_LOG("Insert into geo_knowledge values size: %{public}zu", values.size());
263 int64_t rowNum = 0;
264 int32_t errCode = BatchInsertWithRetry(GEO_KNOWLEDGE_TABLE, values, rowNum);
265 if (errCode != E_OK || rowNum != static_cast<int64_t>(values.size())) {
266 int64_t failNums = static_cast<int64_t>(values.size()) - rowNum;
267 MEDIA_ERR_LOG("Insert into geo_knowledge fail, num: %{public}" PRId64, failNums);
268 ErrorInfo errorInfo(RestoreError::INSERT_FAILED, static_cast<int32_t>(values.size()),
269 "errCode: " + std::to_string(errCode), "Insert into geo_knowledge fail");
270 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
271 cloneRestoreAnalysisTotal_.UpdateRestoreStatusAsFailed();
272 failedCnt_ += failNums;
273 }
274 offset += PAGE_SIZE;
275 successCnt_ += rowNum;
276 } while (offset < infos.size());
277 }
278
GetInfo(GeoCloneInfo & info,std::shared_ptr<NativeRdb::ResultSet> resultSet)279 void CloneRestoreGeo::GetInfo(GeoCloneInfo &info, std::shared_ptr<NativeRdb::ResultSet> resultSet)
280 {
281 info.latitude = BackupDatabaseUtils::GetOptionalValue<double>(resultSet, LATITUDE);
282 info.longitude = BackupDatabaseUtils::GetOptionalValue<double>(resultSet, LONGITUDE);
283 info.locationKey = BackupDatabaseUtils::GetOptionalValue<int64_t>(resultSet, LOCATION_KEY);
284 info.cityId = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, CITY_ID);
285 info.language = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, LANGUAGE);
286 info.country = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, COUNTRY);
287 info.adminArea = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, ADMIN_AREA);
288 info.subAdminArea = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, SUB_ADMIN_AREA);
289 info.locality = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, LOCALITY);
290 info.subLocality = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, SUB_LOCALITY);
291 info.thoroughfare = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, THOROUGHFARE);
292 info.subThoroughfare = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, SUB_THOROUGHFARE);
293 info.featureName = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, FEATURE_NAME);
294 info.cityName = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, CITY_NAME);
295 info.addressDescription = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, ADDRESS_DESCRIPTION);
296 info.aoi = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, AOI);
297 info.poi = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, POI);
298 info.firstAoi = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, FIRST_AOI);
299 info.firstPoi = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, FIRST_POI);
300 info.locationVersion = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, LOCATION_VERSION);
301 info.firstAoiCategory = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, FIRST_AOI_CATEGORY);
302 info.firstPoiCategory = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, FIRST_POI_CATEGORY);
303 info.fileIdOld = BackupDatabaseUtils::GetOptionalValue<int32_t>(resultSet, FILE_ID);
304 info.locationType = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, LOCATION_TYPE);
305 }
306
GetMapInsertValue(NativeRdb::ValuesBucket & value,GeoCloneInfo info,const std::unordered_set<std::string> & intersection)307 void CloneRestoreGeo::GetMapInsertValue(NativeRdb::ValuesBucket &value, GeoCloneInfo info,
308 const std::unordered_set<std::string> &intersection)
309 {
310 PutIfInIntersection(value, LATITUDE, info.latitude, intersection);
311 PutIfInIntersection(value, LONGITUDE, info.longitude, intersection);
312 PutIfInIntersection(value, LOCATION_KEY, info.locationKey, intersection);
313 PutIfInIntersection(value, CITY_ID, info.cityId, intersection);
314 PutIfInIntersection(value, LANGUAGE, info.language, intersection);
315 PutIfInIntersection(value, COUNTRY, info.country, intersection);
316 PutIfInIntersection(value, ADMIN_AREA, info.adminArea, intersection);
317 PutIfInIntersection(value, SUB_ADMIN_AREA, info.subAdminArea, intersection);
318 PutIfInIntersection(value, LOCALITY, info.locality, intersection);
319 PutIfInIntersection(value, SUB_LOCALITY, info.subLocality, intersection);
320 PutIfInIntersection(value, THOROUGHFARE, info.thoroughfare, intersection);
321 PutIfInIntersection(value, SUB_THOROUGHFARE, info.subThoroughfare, intersection);
322 PutIfInIntersection(value, FEATURE_NAME, info.featureName, intersection);
323 PutIfInIntersection(value, CITY_NAME, info.cityName, intersection);
324 PutIfInIntersection(value, ADDRESS_DESCRIPTION, info.addressDescription, intersection);
325 PutIfInIntersection(value, AOI, info.aoi, intersection);
326 PutIfInIntersection(value, POI, info.poi, intersection);
327 PutIfInIntersection(value, FIRST_AOI, info.firstAoi, intersection);
328 PutIfInIntersection(value, FIRST_POI, info.firstPoi, intersection);
329 PutIfInIntersection(value, LOCATION_VERSION, info.locationVersion, intersection);
330 PutIfInIntersection(value, FIRST_AOI_CATEGORY, info.firstAoiCategory, intersection);
331 PutIfInIntersection(value, FIRST_POI_CATEGORY, info.firstPoiCategory, intersection);
332 PutIfInIntersection(value, FILE_ID, info.fileIdNew, intersection);
333 PutIfInIntersection(value, LOCATION_TYPE, info.locationType, intersection);
334 }
335
CheckTableColumns(const std::string & tableName,std::unordered_map<std::string,std::string> & columns)336 bool CloneRestoreGeo::CheckTableColumns(const std::string& tableName,
337 std::unordered_map<std::string, std::string>& columns)
338 {
339 std::unordered_map<std::string, std::string> srcColumnInfoMap =
340 BackupDatabaseUtils::GetColumnInfoMap(mediaRdb_, tableName);
341 for (auto it = columns.begin(); it != columns.end(); ++it) {
342 CHECK_AND_CONTINUE(srcColumnInfoMap.find(it->first) == srcColumnInfoMap.end());
343 return false;
344 }
345 return true;
346 }
347
GetCommonColumns(const string & tableName)348 std::unordered_set<std::string> CloneRestoreGeo::GetCommonColumns(const string &tableName)
349 {
350 std::unordered_map<std::string, std::string> srcColumnInfoMap =
351 BackupDatabaseUtils::GetColumnInfoMap(mediaRdb_, tableName);
352 std::unordered_map<std::string, std::string> dstColumnInfoMap =
353 BackupDatabaseUtils::GetColumnInfoMap(mediaLibraryRdb_, tableName);
354 std::unordered_set<std::string> result;
355 auto comparedColumns = GetValueFromMap(COMPARED_COLUMNS_MAP, tableName);
356 for (auto it = dstColumnInfoMap.begin(); it != dstColumnInfoMap.end(); ++it) {
357 bool cond = srcColumnInfoMap.find(it->first) != srcColumnInfoMap.end() && comparedColumns.count(it->first) > 0;
358 CHECK_AND_EXECUTE(!cond, result.insert(it->first));
359 }
360 return result;
361 }
362
BatchInsertWithRetry(const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & values,int64_t & rowNum)363 int32_t CloneRestoreGeo::BatchInsertWithRetry(const std::string &tableName,
364 std::vector<NativeRdb::ValuesBucket> &values, int64_t &rowNum)
365 {
366 CHECK_AND_RETURN_RET(!values.empty(), E_OK);
367 int32_t errCode = E_ERR;
368 TransactionOperations trans{ __func__ };
369 trans.SetBackupRdbStore(mediaLibraryRdb_);
370 std::function<int(void)> func = [&]()->int {
371 errCode = trans.BatchInsert(rowNum, tableName, values);
372 CHECK_AND_PRINT_LOG(errCode == E_OK, "InsertSql failed, errCode: %{public}d, rowNum: %{public}ld.",
373 errCode, (long)rowNum);
374 return errCode;
375 };
376 errCode = trans.RetryTrans(func, true);
377 CHECK_AND_PRINT_LOG(errCode == E_OK, "BatchInsertWithRetry: tans finish fail!, ret:%{public}d", errCode);
378 return errCode;
379 }
380
ReportRestoreTask()381 void CloneRestoreGeo::ReportRestoreTask()
382 {
383 ReportRestoreTaskOfTotal();
384 ReportRestoreTaskofData();
385 }
386
ReportRestoreTaskOfTotal()387 void CloneRestoreGeo::ReportRestoreTaskOfTotal()
388 {
389 RestoreTaskInfo info;
390 cloneRestoreAnalysisTotal_.SetRestoreTaskInfo(info);
391 info.type = "CLONE_RESTORE_GEO_TOTAL";
392 info.errorCode = std::to_string(GEO_STATUS_SUCCESS);
393 info.errorInfo = "timeCost: " + std::to_string(restoreTimeCost_);
394 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).Report(info);
395 }
396
ReportRestoreTaskofData()397 void CloneRestoreGeo::ReportRestoreTaskofData()
398 {
399 RestoreTaskInfo info;
400 info.type = "CLONE_RESTORE_GEO_DATA";
401 info.errorCode = std::to_string(GEO_STATUS_SUCCESS);
402 info.errorInfo = "max_id: " + std::to_string(maxId_);
403 info.successCount = successCnt_;
404 info.failedCount = failedCnt_;
405 info.duplicateCount = duplicateCnt_;
406 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).Report(info);
407 }
408 } // namespace OHOS::Media