• 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 #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