1 /*
2  * Copyright (C) 2023-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 
16 #define MLOG_TAG "MediaLibraryBackupUtils"
17 
18 #include "backup_database_utils.h"
19 
20 #include <nlohmann/json.hpp>
21 #include <safe_map.h>
22 
23 #include "backup_const_column.h"
24 #include "media_file_utils.h"
25 #include "media_log.h"
26 #include "medialibrary_errno.h"
27 #include "result_set_utils.h"
28 
29 namespace OHOS {
30 namespace Media {
31 const int32_t SCALE_FACTOR = 2;
32 const int32_t SCALE_MIN_SIZE = 1080;
33 const int32_t SCALE_MAX_SIZE = 2560;
34 const int32_t UPDATE_COUNT = 200;
35 const float SCALE_DEFAULT = 0.25;
36 const size_t MIN_GARBLE_SIZE = 2;
37 const size_t GARBLE_START = 1;
38 const size_t XY_DIMENSION = 2;
39 const size_t BYTE_LEN = 4;
40 const size_t BYTE_BASE_OFFSET = 8;
41 const size_t LANDMARKS_SIZE = 5;
42 const std::string LANDMARK_X = "x";
43 const std::string LANDMARK_Y = "y";
44 const std::string COLUMN_INTEGRITY_CHECK = "quick_check";
45 const std::vector<uint32_t> HEX_MAX = { 0xff, 0xffff, 0xffffff, 0xffffffff };
46 static SafeMap<int32_t, int32_t> fileIdOld2NewForCloudEnhancement;
47 
InitDb(std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & dbName,const std::string & dbPath,const std::string & bundleName,bool isMediaLibrary,int32_t area)48 int32_t BackupDatabaseUtils::InitDb(std::shared_ptr<NativeRdb::RdbStore> &rdbStore, const std::string &dbName,
49     const std::string &dbPath, const std::string &bundleName, bool isMediaLibrary, int32_t area)
50 {
51     NativeRdb::RdbStoreConfig config(dbName);
52     config.SetPath(dbPath);
53     config.SetBundleName(bundleName);
54     config.SetReadConSize(CONNECT_SIZE);
55     config.SetSecurityLevel(NativeRdb::SecurityLevel::S3);
56     config.SetHaMode(NativeRdb::HAMode::MANUAL_TRIGGER);
57     config.SetAllowRebuild(true);
58     if (area != DEFAULT_AREA_VERSION) {
59         config.SetArea(area);
60     }
61     if (isMediaLibrary) {
62         config.SetScalarFunction("cloud_sync_func", 0, CloudSyncTriggerFunc);
63         config.SetScalarFunction("is_caller_self_func", 0, IsCallerSelfFunc);
64         config.SetScalarFunction("photo_album_notify_func", 1, PhotoAlbumNotifyFunc);
65     }
66     int32_t err;
67     RdbCallback cb;
68     rdbStore = NativeRdb::RdbHelper::GetRdbStore(config, MEDIA_RDB_VERSION, cb, err);
69     return err;
70 }
71 
CloudSyncTriggerFunc(const std::vector<std::string> & args)72 std::string BackupDatabaseUtils::CloudSyncTriggerFunc(const std::vector<std::string> &args)
73 {
74     return "";
75 }
76 
IsCallerSelfFunc(const std::vector<std::string> & args)77 std::string BackupDatabaseUtils::IsCallerSelfFunc(const std::vector<std::string> &args)
78 {
79     return "false";
80 }
81 
PhotoAlbumNotifyFunc(const std::vector<std::string> & args)82 std::string BackupDatabaseUtils::PhotoAlbumNotifyFunc(const std::vector<std::string> &args)
83 {
84     return "";
85 }
86 
ExecSqlWithRetry(std::function<int32_t ()> execSql)87 static int32_t ExecSqlWithRetry(std::function<int32_t()> execSql)
88 {
89     int32_t currentTime = 0;
90     int32_t err = NativeRdb::E_OK;
91     while (currentTime <= MAX_TRY_TIMES) {
92         err = execSql();
93         if (err == NativeRdb::E_OK) {
94             break;
95         } else if (err == NativeRdb::E_SQLITE_LOCKED || err == NativeRdb::E_DATABASE_BUSY ||
96             err == NativeRdb::E_SQLITE_BUSY) {
97             std::this_thread::sleep_for(std::chrono::milliseconds(TRANSACTION_WAIT_INTERVAL));
98             currentTime++;
99             MEDIA_ERR_LOG("execSql busy, err: %{public}d, currentTime: %{public}d", err, currentTime);
100         } else {
101             MEDIA_ERR_LOG("execSql failed, err: %{public}d, currentTime: %{public}d", err, currentTime);
102             break;
103         }
104     }
105     return err;
106 }
107 
QueryInt(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::string & sql,const std::string & column)108 int32_t BackupDatabaseUtils::QueryInt(std::shared_ptr<NativeRdb::RdbStore> rdbStore, const std::string &sql,
109     const std::string &column)
110 {
111     if (rdbStore == nullptr) {
112         MEDIA_ERR_LOG("rdb_ is nullptr, Maybe init failed.");
113         return 0;
114     }
115     auto resultSet = rdbStore->QuerySql(sql);
116     if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
117         return 0;
118     }
119     int32_t result = GetInt32Val(column, resultSet);
120     return result;
121 }
122 
Update(std::shared_ptr<NativeRdb::RdbStore> & rdbStore,int32_t & changeRows,NativeRdb::ValuesBucket & valuesBucket,std::unique_ptr<NativeRdb::AbsRdbPredicates> & predicates)123 int32_t BackupDatabaseUtils::Update(std::shared_ptr<NativeRdb::RdbStore> &rdbStore, int32_t &changeRows,
124     NativeRdb::ValuesBucket &valuesBucket, std::unique_ptr<NativeRdb::AbsRdbPredicates> &predicates)
125 {
126     if (rdbStore == nullptr) {
127         MEDIA_ERR_LOG("rdb_ is nullptr, Maybe init failed.");
128         return E_FAIL;
129     }
130     return ExecSqlWithRetry([&]() { return rdbStore->Update(changeRows, valuesBucket, *predicates); });
131 }
132 
Delete(NativeRdb::AbsRdbPredicates & predicates,int32_t & changeRows,std::shared_ptr<NativeRdb::RdbStore> & rdbStore)133 int32_t BackupDatabaseUtils::Delete(NativeRdb::AbsRdbPredicates &predicates, int32_t &changeRows,
134     std::shared_ptr<NativeRdb::RdbStore> &rdbStore)
135 {
136     if (rdbStore == nullptr) {
137         MEDIA_ERR_LOG("rdb is nullptr");
138         return E_FAIL;
139     }
140     return ExecSqlWithRetry([&]() { return rdbStore->Delete(changeRows, predicates); });
141 }
142 
InitGarbageAlbum(std::shared_ptr<NativeRdb::RdbStore> galleryRdb,std::set<std::string> & cacheSet,std::unordered_map<std::string,std::string> & nickMap)143 int32_t BackupDatabaseUtils::InitGarbageAlbum(std::shared_ptr<NativeRdb::RdbStore> galleryRdb,
144     std::set<std::string> &cacheSet, std::unordered_map<std::string, std::string> &nickMap)
145 {
146     if (galleryRdb == nullptr) {
147         MEDIA_ERR_LOG("Pointer rdb_ is nullptr, Maybe init failed.");
148         return E_FAIL;
149     }
150 
151     const string querySql = "SELECT nick_dir, nick_name FROM garbage_album where type = 0";
152     auto resultSet = galleryRdb->QuerySql(QUERY_GARBAGE_ALBUM);
153     if (resultSet == nullptr) {
154         return E_HAS_DB_ERROR;
155     }
156     int32_t count = -1;
157     int32_t err = resultSet->GetRowCount(count);
158     if (err != E_OK) {
159         MEDIA_ERR_LOG("Failed to get count, err: %{public}d", err);
160         return E_FAIL;
161     }
162     MEDIA_INFO_LOG("garbageCount: %{public}d", count);
163     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
164         int32_t type;
165         resultSet->GetInt(INDEX_TYPE, type);
166         if (type == NICK) {
167             string nickName;
168             string nickDir;
169             resultSet->GetString(INDEX_NICK_DIR, nickDir);
170             resultSet->GetString(INDEX_NICK_NAME, nickName);
171             nickMap[nickDir] = nickName;
172         } else {
173             string cacheDir;
174             resultSet->GetString(INDEX_CACHE_DIR, cacheDir);
175             cacheSet.insert(cacheDir);
176         }
177     }
178     MEDIA_INFO_LOG("add map success!");
179     resultSet->Close();
180     return E_OK;
181 }
182 
QueryGalleryCloneCount(std::shared_ptr<NativeRdb::RdbStore> galleryRdb)183 int32_t BackupDatabaseUtils::QueryGalleryCloneCount(std::shared_ptr<NativeRdb::RdbStore> galleryRdb)
184 {
185     static string QUERY_GALLERY_CLONE_COUNT =
186         string("SELECT count(1) AS count FROM gallery_media WHERE local_media_id = -3 AND _size > 0 ") +
187         "AND (storage_id IN (0, 65537)) AND relative_bucket_id NOT IN ( " +
188         "SELECT DISTINCT relative_bucket_id FROM garbage_album WHERE type = 1)";
189     return QueryInt(galleryRdb, QUERY_GALLERY_CLONE_COUNT, CUSTOM_COUNT);
190 }
191 
QueryGalleryDuplicateDataCount(std::shared_ptr<NativeRdb::RdbStore> galleryRdb,int32_t & count,int32_t & total)192 void BackupDatabaseUtils::QueryGalleryDuplicateDataCount(std::shared_ptr<NativeRdb::RdbStore> galleryRdb,
193     int32_t &count, int32_t &total)
194 {
195     static string QUERY_GALLERY_DUPLICATE_DATA_COUNT = "SELECT count(DISTINCT _data) as count, count(1) as total"
196         " FROM gallery_media WHERE _data IN (SELECT _data FROM gallery_media GROUP BY _data HAVING count(1) > 1)";
197     auto resultSet = GetQueryResultSet(galleryRdb, QUERY_GALLERY_DUPLICATE_DATA_COUNT);
198     if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
199         return;
200     }
201     count = GetInt32Val("count", resultSet);
202     total = GetInt32Val("total", resultSet);
203 }
204 
GetQueryResultSet(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & querySql,const std::vector<std::string> & sqlArgs)205 std::shared_ptr<NativeRdb::ResultSet> BackupDatabaseUtils::GetQueryResultSet(
206     const std::shared_ptr<NativeRdb::RdbStore> &rdbStore, const std::string &querySql,
207     const std::vector<std::string> &sqlArgs)
208 {
209     if (rdbStore == nullptr) {
210         MEDIA_ERR_LOG("rdbStore is nullptr");
211         return nullptr;
212     }
213     return rdbStore->QuerySql(querySql, sqlArgs);
214 }
215 
GetColumnInfoMap(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & tableName)216 std::unordered_map<std::string, std::string> BackupDatabaseUtils::GetColumnInfoMap(
217     const std::shared_ptr<NativeRdb::RdbStore> &rdbStore, const std::string &tableName)
218 {
219     std::unordered_map<std::string, std::string> columnInfoMap;
220     std::string querySql = "SELECT name, type FROM pragma_table_info('" + tableName + "')";
221     auto resultSet = GetQueryResultSet(rdbStore, querySql);
222     if (resultSet == nullptr) {
223         MEDIA_ERR_LOG("resultSet is nullptr");
224         return columnInfoMap;
225     }
226     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
227         std::string columnName = GetStringVal(PRAGMA_TABLE_NAME, resultSet);
228         std::string columnType = GetStringVal(PRAGMA_TABLE_TYPE, resultSet);
229         if (columnName.empty() || columnType.empty()) {
230             MEDIA_ERR_LOG("Empty column name or type: %{public}s, %{public}s", columnName.c_str(), columnType.c_str());
231             continue;
232         }
233         columnInfoMap[columnName] = columnType;
234     }
235     return columnInfoMap;
236 }
237 
GarbleInfoName(const string & infoName)238 std::string BackupDatabaseUtils::GarbleInfoName(const string &infoName)
239 {
240     std::string garbledInfoName = infoName;
241     if (infoName.size() <= MIN_GARBLE_SIZE) {
242         return garbledInfoName;
243     }
244     size_t garbledSize = infoName.size() - MIN_GARBLE_SIZE;
245     garbledInfoName.replace(GARBLE_START, garbledSize, GARBLE);
246     return garbledInfoName;
247 }
248 
UpdateUniqueNumber(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,int32_t number,const std::string & type)249 void BackupDatabaseUtils::UpdateUniqueNumber(const std::shared_ptr<NativeRdb::RdbStore> &rdbStore, int32_t number,
250     const std::string &type)
251 {
252     const string updateSql =
253         "UPDATE UniqueNumber SET unique_number = " + to_string(number) + " WHERE media_type = '" + type + "'";
254     int32_t erroCode = BackupDatabaseUtils::ExecuteSQL(rdbStore, updateSql);
255     if (erroCode < 0) {
256         MEDIA_ERR_LOG("execute update unique number failed, ret=%{public}d", erroCode);
257     }
258 }
259 
QueryUniqueNumber(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & type)260 int32_t BackupDatabaseUtils::QueryUniqueNumber(const std::shared_ptr<NativeRdb::RdbStore> &rdbStore,
261     const std::string &type)
262 {
263     const string querySql = "SELECT unique_number FROM UniqueNumber WHERE media_type = '" + type + "'";
264     return QueryInt(rdbStore, querySql, UNIQUE_NUMBER);
265 }
266 
UpdateSelection(std::string & selection,const std::string & selectionToAdd,bool needWrap)267 void BackupDatabaseUtils::UpdateSelection(std::string &selection, const std::string &selectionToAdd, bool needWrap)
268 {
269     if (selectionToAdd.empty()) {
270         return;
271     }
272     std::string wrappedSelectionToAdd = needWrap ? "'" + selectionToAdd + "'" : selectionToAdd;
273     selection += selection.empty() ? wrappedSelectionToAdd : ", " + wrappedSelectionToAdd;
274 }
275 
UpdateSdWhereClause(std::string & querySql,bool shouldIncludeSd)276 void BackupDatabaseUtils::UpdateSdWhereClause(std::string &querySql, bool shouldIncludeSd)
277 {
278     if (shouldIncludeSd) {
279         return;
280     }
281     querySql += " AND " + EXCLUDE_SD;
282 }
283 
GetBlob(const std::string & columnName,std::shared_ptr<NativeRdb::ResultSet> resultSet,std::vector<uint8_t> & blobVal)284 int32_t BackupDatabaseUtils::GetBlob(const std::string &columnName, std::shared_ptr<NativeRdb::ResultSet> resultSet,
285     std::vector<uint8_t> &blobVal)
286 {
287     int32_t columnIndex = 0;
288     int32_t errCode = resultSet->GetColumnIndex(columnName, columnIndex);
289     if (errCode) {
290         MEDIA_ERR_LOG("Get column index errCode: %{public}d", errCode);
291         return E_FAIL;
292     }
293     if (resultSet->GetBlob(columnIndex, blobVal) != NativeRdb::E_OK) {
294         return E_FAIL;
295     }
296     return E_OK;
297 }
298 
GetLandmarksStr(const std::string & columnName,std::shared_ptr<NativeRdb::ResultSet> resultSet)299 std::string BackupDatabaseUtils::GetLandmarksStr(const std::string &columnName,
300     std::shared_ptr<NativeRdb::ResultSet> resultSet)
301 {
302     std::vector<uint8_t> blobVal;
303     if (GetBlob(columnName, resultSet, blobVal) != E_OK) {
304         MEDIA_ERR_LOG("Get blob failed");
305         return "";
306     }
307     return GetLandmarksStr(blobVal);
308 }
309 
GetLandmarksStr(const std::vector<uint8_t> & bytes)310 std::string BackupDatabaseUtils::GetLandmarksStr(const std::vector<uint8_t> &bytes)
311 {
312     if (bytes.size() != LANDMARKS_SIZE * XY_DIMENSION * BYTE_LEN) {
313         MEDIA_ERR_LOG("Get landmarks bytes size: %{public}zu, not %{public}zu", bytes.size(),
314             LANDMARKS_SIZE * XY_DIMENSION * BYTE_LEN);
315         return "";
316     }
317     nlohmann::json landmarksJson;
318     for (size_t index = 0; index < bytes.size(); index += XY_DIMENSION * BYTE_LEN) {
319         nlohmann::json landmarkJson;
320         landmarkJson[LANDMARK_X] = GetUint32ValFromBytes(bytes, index);
321         landmarkJson[LANDMARK_Y] = GetUint32ValFromBytes(bytes, index + BYTE_LEN);
322         landmarksJson.push_back(landmarkJson);
323     }
324     return landmarksJson.dump();
325 }
326 
GetUint32ValFromBytes(const std::vector<uint8_t> & bytes,size_t start)327 uint32_t BackupDatabaseUtils::GetUint32ValFromBytes(const std::vector<uint8_t> &bytes, size_t start)
328 {
329     uint32_t uint32Val = 0;
330     for (size_t index = 0; index < BYTE_LEN; index++) {
331         uint32Val |= static_cast<uint32_t>(bytes[start + index]) << (index * BYTE_BASE_OFFSET);
332         uint32Val &= HEX_MAX[index];
333     }
334     return uint32Val;
335 }
336 
UpdateAnalysisTotalStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)337 void BackupDatabaseUtils::UpdateAnalysisTotalStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)
338 {
339     std::string updateSql = "UPDATE tab_analysis_total SET face = CASE WHEN EXISTS \
340         (SELECT 1 FROM tab_analysis_image_face WHERE tab_analysis_image_face.file_id = tab_analysis_total.file_id \
341         AND tag_id = '-1') THEN 2 ELSE 3 END WHERE EXISTS (SELECT 1 FROM tab_analysis_image_face WHERE \
342         tab_analysis_image_face.file_id = tab_analysis_total.file_id)";
343     int32_t errCode = BackupDatabaseUtils::ExecuteSQL(rdbStore, updateSql);
344     if (errCode < 0) {
345         MEDIA_ERR_LOG("execute update analysis total failed, ret=%{public}d", errCode);
346     }
347 }
348 
UpdateAnalysisFaceTagStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)349 void BackupDatabaseUtils::UpdateAnalysisFaceTagStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)
350 {
351     std::string updateSql = "UPDATE tab_analysis_face_tag SET count = (SELECT count(1) from tab_analysis_image_face \
352         WHERE tab_analysis_image_face.tag_id = tab_analysis_face_tag.tag_id)";
353     int32_t errCode = BackupDatabaseUtils::ExecuteSQL(rdbStore, updateSql);
354     if (errCode < 0) {
355         MEDIA_ERR_LOG("execute update analysis face tag count failed, ret=%{public}d", errCode);
356     }
357 }
358 
UpdateAnalysisTotalTblStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::vector<FileIdPair> & fileIdPair)359 void BackupDatabaseUtils::UpdateAnalysisTotalTblStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore,
360     const std::vector<FileIdPair>& fileIdPair)
361 {
362     std::string fileIdNewFilterClause = GetFileIdNewFilterClause(rdbStore, fileIdPair);
363     std::string updateSql =
364         "UPDATE tab_analysis_total "
365         "SET face = CASE "
366             "WHEN EXISTS (SELECT 1 FROM tab_analysis_image_face "
367                          "WHERE tab_analysis_image_face.file_id = tab_analysis_total.file_id "
368                          "AND tag_id = '-1') THEN 2 "
369             "WHEN EXISTS (SELECT 1 FROM tab_analysis_image_face "
370                          "WHERE tab_analysis_image_face.file_id = tab_analysis_total.file_id "
371                          "AND tag_id = '-2') THEN 4 "
372             "ELSE 3 "
373         "END "
374         "WHERE EXISTS (SELECT 1 FROM tab_analysis_image_face "
375                       "WHERE tab_analysis_image_face.file_id = tab_analysis_total.file_id "
376                       "AND " + IMAGE_FACE_COL_FILE_ID + " IN " + fileIdNewFilterClause + ")";
377 
378     int32_t errCode = BackupDatabaseUtils::ExecuteSQL(rdbStore, updateSql);
379     if (errCode < 0) {
380         MEDIA_ERR_LOG("execute update analysis total failed, ret=%{public}d", errCode);
381     }
382 }
383 
UpdateFaceAnalysisTblStatus(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)384 void BackupDatabaseUtils::UpdateFaceAnalysisTblStatus(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
385 {
386     BackupDatabaseUtils::UpdateAnalysisFaceTagStatus(mediaLibraryRdb);
387 }
388 
SetTagIdNew(PortraitAlbumInfo & portraitAlbumInfo,std::unordered_map<std::string,std::string> & tagIdMap)389 bool BackupDatabaseUtils::SetTagIdNew(PortraitAlbumInfo &portraitAlbumInfo,
390     std::unordered_map<std::string, std::string> &tagIdMap)
391 {
392     portraitAlbumInfo.tagIdNew = TAG_ID_PREFIX + std::to_string(MediaFileUtils::UTCTimeNanoSeconds());
393     tagIdMap[portraitAlbumInfo.tagIdOld] = portraitAlbumInfo.tagIdNew;
394     return true;
395 }
396 
SetLandmarks(FaceInfo & faceInfo,const std::unordered_map<std::string,FileInfo> & fileInfoMap)397 bool BackupDatabaseUtils::SetLandmarks(FaceInfo &faceInfo, const std::unordered_map<std::string, FileInfo> &fileInfoMap)
398 {
399     if (faceInfo.hash.empty() || fileInfoMap.count(faceInfo.hash) == 0) {
400         MEDIA_ERR_LOG("Set landmarks for face %{public}s failed, no such file hash", faceInfo.faceId.c_str());
401         return false;
402     }
403     FileInfo fileInfo = fileInfoMap.at(faceInfo.hash);
404     if (fileInfo.width == 0 || fileInfo.height == 0) {
405         MEDIA_ERR_LOG("Set landmarks for face %{public}s failed, invalid width %{public}d or height %{public}d",
406             faceInfo.faceId.c_str(), fileInfo.width, fileInfo.height);
407         return false;
408     }
409     float scale = GetLandmarksScale(fileInfo.width, fileInfo.height);
410     if (scale == 0) {
411         MEDIA_ERR_LOG("Set landmarks for face %{public}s failed, scale = 0", faceInfo.faceId.c_str());
412         return false;
413     }
414     nlohmann::json landmarksJson = nlohmann::json::parse(faceInfo.landmarks, nullptr, false);
415     if (landmarksJson.is_discarded()) {
416         MEDIA_ERR_LOG("Set landmarks for face %{public}s failed, parse landmarks failed", faceInfo.faceId.c_str());
417         return false;
418     }
419     for (auto &landmark : landmarksJson) {
420         if (!landmark.contains(LANDMARK_X) || !landmark.contains(LANDMARK_Y)) {
421             MEDIA_ERR_LOG("Set landmarks for face %{public}s failed, lack of x or y", faceInfo.faceId.c_str());
422             return false;
423         }
424         landmark[LANDMARK_X] = static_cast<float>(landmark[LANDMARK_X]) / fileInfo.width / scale;
425         landmark[LANDMARK_Y] = static_cast<float>(landmark[LANDMARK_Y]) / fileInfo.height / scale;
426         if (IsLandmarkValid(faceInfo, landmark[LANDMARK_X], landmark[LANDMARK_Y])) {
427             continue;
428         }
429         MEDIA_WARN_LOG("Given landmark may be invalid, (%{public}f, %{public}f), rect TL: (%{public}f, %{public}f), "
430             "rect BR: (%{public}f, %{public}f)", static_cast<float>(landmark[LANDMARK_X]),
431             static_cast<float>(landmark[LANDMARK_Y]), faceInfo.scaleX, faceInfo.scaleY,
432             faceInfo.scaleX + faceInfo.scaleWidth, faceInfo.scaleY + faceInfo.scaleHeight);
433     }
434     faceInfo.landmarks = landmarksJson.dump();
435     return true;
436 }
437 
SetFileIdNew(FaceInfo & faceInfo,const std::unordered_map<std::string,FileInfo> & fileInfoMap)438 bool BackupDatabaseUtils::SetFileIdNew(FaceInfo &faceInfo, const std::unordered_map<std::string, FileInfo> &fileInfoMap)
439 {
440     if (faceInfo.hash.empty() || fileInfoMap.count(faceInfo.hash) == 0) {
441         MEDIA_ERR_LOG("Set new file_id for face %{public}s failed, no such file hash", faceInfo.faceId.c_str());
442         return false;
443     }
444     faceInfo.fileIdNew = fileInfoMap.at(faceInfo.hash).fileIdNew;
445     if (faceInfo.fileIdNew <= 0) {
446         MEDIA_ERR_LOG("Set new file_id for face %{public}s failed, file_id %{public}d <= 0", faceInfo.faceId.c_str(),
447             faceInfo.fileIdNew);
448         return false;
449     }
450     return true;
451 }
452 
SetTagIdNew(FaceInfo & faceInfo,const std::unordered_map<std::string,std::string> & tagIdMap)453 bool BackupDatabaseUtils::SetTagIdNew(FaceInfo &faceInfo, const std::unordered_map<std::string, std::string> &tagIdMap)
454 {
455     if (faceInfo.tagIdOld.empty()) {
456         MEDIA_ERR_LOG("Set new tag_id for face %{public}s failed, empty tag_id", faceInfo.faceId.c_str());
457         return false;
458     }
459     if (tagIdMap.count(faceInfo.tagIdOld) == 0) {
460         faceInfo.tagIdNew = TAG_ID_UNPROCESSED;
461         return true;
462     }
463     faceInfo.tagIdNew = tagIdMap.at(faceInfo.tagIdOld);
464     if (faceInfo.tagIdNew.empty() || !MediaFileUtils::StartsWith(faceInfo.tagIdNew, TAG_ID_PREFIX)) {
465         MEDIA_ERR_LOG("Set new tag_id for face %{public}s failed, new tag_id %{public}s empty or invalid",
466             faceInfo.tagIdNew.c_str(), faceInfo.faceId.c_str());
467         return false;
468     }
469     return true;
470 }
471 
SetAlbumIdNew(FaceInfo & faceInfo,const std::unordered_map<std::string,int32_t> & albumIdMap)472 bool BackupDatabaseUtils::SetAlbumIdNew(FaceInfo &faceInfo, const std::unordered_map<std::string, int32_t> &albumIdMap)
473 {
474     if (faceInfo.tagIdNew == TAG_ID_UNPROCESSED) {
475         return true;
476     }
477     if (albumIdMap.count(faceInfo.tagIdNew) == 0) {
478         MEDIA_ERR_LOG("Set new album_id for face %{public}s failed, no such tag_id", faceInfo.faceId.c_str());
479         return false;
480     }
481     faceInfo.albumIdNew = albumIdMap.at(faceInfo.tagIdNew);
482     if (faceInfo.albumIdNew <= 0) {
483         MEDIA_ERR_LOG("Set new album_id for face %{public}s failed, album_id %{public}d <= 0", faceInfo.faceId.c_str(),
484             faceInfo.albumIdNew);
485         return false;
486     }
487     return true;
488 }
489 
PrintErrorLog(const std::string & errorLog,int64_t start)490 void BackupDatabaseUtils::PrintErrorLog(const std::string &errorLog, int64_t start)
491 {
492     int64_t end = MediaFileUtils::UTCTimeMilliSeconds();
493     MEDIA_INFO_LOG("%{public}s, cost %{public}ld", errorLog.c_str(), (long)(end - start));
494 }
495 
GetLandmarksScale(int32_t width,int32_t height)496 float BackupDatabaseUtils::GetLandmarksScale(int32_t width, int32_t height)
497 {
498     float scale = 1;
499     int32_t minWidthHeight = width <= height ? width : height;
500     if (minWidthHeight >= SCALE_MIN_SIZE * SCALE_FACTOR) {
501         minWidthHeight = static_cast<int32_t>(minWidthHeight * SCALE_DEFAULT);
502         scale = SCALE_DEFAULT;
503         if (minWidthHeight < SCALE_MIN_SIZE) {
504             minWidthHeight *= SCALE_FACTOR;
505             scale *= SCALE_FACTOR;
506         }
507         if (minWidthHeight < SCALE_MIN_SIZE) {
508             scale = 1;
509         }
510     }
511     width = static_cast<int32_t>(width * scale);
512     height = static_cast<int32_t>(height * scale);
513     int32_t maxWidthHeight = width >= height ? width : height;
514     scale *= maxWidthHeight >= SCALE_MAX_SIZE ? static_cast<float>(SCALE_MAX_SIZE) / maxWidthHeight : 1;
515     return scale;
516 }
517 
IsLandmarkValid(const FaceInfo & faceInfo,float landmarkX,float landmarkY)518 bool BackupDatabaseUtils::IsLandmarkValid(const FaceInfo &faceInfo, float landmarkX, float landmarkY)
519 {
520     return IsValInBound(landmarkX, faceInfo.scaleX, faceInfo.scaleX + faceInfo.scaleWidth) &&
521         IsValInBound(landmarkY, faceInfo.scaleY, faceInfo.scaleY + faceInfo.scaleHeight);
522 }
523 
IsValInBound(float val,float minVal,float maxVal)524 bool BackupDatabaseUtils::IsValInBound(float val, float minVal, float maxVal)
525 {
526     return val >= minVal && val <= maxVal;
527 }
528 
GetColumnInfoPairs(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & tableName)529 std::vector<std::pair<std::string, std::string>> BackupDatabaseUtils::GetColumnInfoPairs(
530     const std::shared_ptr<NativeRdb::RdbStore> &rdbStore, const std::string &tableName)
531 {
532     std::vector<std::pair<std::string, std::string>> columnInfoPairs;
533     std::string querySql = "SELECT name, type FROM pragma_table_info('" + tableName + "')";
534     auto resultSet = GetQueryResultSet(rdbStore, querySql);
535     if (resultSet == nullptr) {
536         MEDIA_ERR_LOG("resultSet is nullptr");
537         return columnInfoPairs;
538     }
539     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
540         std::string columnName = GetStringVal(PRAGMA_TABLE_NAME, resultSet);
541         std::string columnType = GetStringVal(PRAGMA_TABLE_TYPE, resultSet);
542         if (columnName.empty() || columnType.empty()) {
543             MEDIA_ERR_LOG("Empty column name or type: %{public}s, %{public}s", columnName.c_str(), columnType.c_str());
544             continue;
545         }
546         columnInfoPairs.emplace_back(columnName, columnType);
547     }
548 
549     return columnInfoPairs;
550 }
551 
GetCommonColumnInfos(std::shared_ptr<NativeRdb::RdbStore> mediaRdb,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,std::string tableName)552 std::vector<std::string> BackupDatabaseUtils::GetCommonColumnInfos(std::shared_ptr<NativeRdb::RdbStore> mediaRdb,
553     std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb, std::string tableName)
554 {
555     std::vector<std::string> commonColumns;
556     auto mediaRdbColumnInfoPairs = BackupDatabaseUtils::GetColumnInfoPairs(mediaRdb, tableName);
557     auto mediaLibraryRdbColumnInfoPairs = BackupDatabaseUtils::GetColumnInfoPairs(mediaLibraryRdb, tableName);
558 
559     for (const auto &pair : mediaRdbColumnInfoPairs) {
560         auto it = std::find_if(mediaLibraryRdbColumnInfoPairs.begin(), mediaLibraryRdbColumnInfoPairs.end(),
561             [&](const std::pair<std::string, std::string> &p) {
562                 return p.first == pair.first && p.second == pair.second;
563             });
564         if (it != mediaLibraryRdbColumnInfoPairs.end()) {
565             commonColumns.emplace_back(pair.first);
566         }
567     }
568 
569     return commonColumns;
570 }
571 
filterColumns(const std::vector<std::string> & allColumns,const std::vector<std::string> & excludedColumns)572 std::vector<std::string> BackupDatabaseUtils::filterColumns(const std::vector<std::string>& allColumns,
573     const std::vector<std::string>& excludedColumns)
574 {
575     std::vector<std::string> filteredColumns;
576     std::copy_if(allColumns.begin(), allColumns.end(), std::back_inserter(filteredColumns),
577         [&excludedColumns](const std::string& column) {
578             return std::find(excludedColumns.begin(), excludedColumns.end(), column) == excludedColumns.end();
579         });
580     return filteredColumns;
581 }
582 
UpdateAnalysisPhotoMapStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)583 void BackupDatabaseUtils::UpdateAnalysisPhotoMapStatus(std::shared_ptr<NativeRdb::RdbStore> rdbStore)
584 {
585     std::string insertSql =
586         "INSERT OR REPLACE INTO AnalysisPhotoMap (map_album, map_asset) "
587         "SELECT AnalysisAlbum.album_id, tab_analysis_image_face.file_id "
588         "FROM AnalysisAlbum "
589         "INNER JOIN tab_analysis_image_face ON AnalysisAlbum.tag_id = tab_analysis_image_face.tag_id";
590 
591     int32_t ret = BackupDatabaseUtils::ExecuteSQL(rdbStore, insertSql);
592     if (ret < 0) {
593         MEDIA_ERR_LOG("execute update AnalysisPhotoMap failed, ret=%{public}d", ret);
594     }
595 }
596 
CollectFileIdPairs(const std::vector<FileInfo> & fileInfos)597 std::vector<FileIdPair> BackupDatabaseUtils::CollectFileIdPairs(const std::vector<FileInfo>& fileInfos)
598 {
599     std::set<FileIdPair> uniquePairs;
600 
601     for (const auto& fileInfo : fileInfos) {
602         uniquePairs.emplace(fileInfo.fileIdOld, fileInfo.fileIdNew);
603     }
604 
605     return std::vector<FileIdPair>(uniquePairs.begin(), uniquePairs.end());
606 }
607 
UnzipFileIdPairs(const std::vector<FileIdPair> & pairs)608 std::pair<std::vector<int32_t>, std::vector<int32_t>> BackupDatabaseUtils::UnzipFileIdPairs(
609     const std::vector<FileIdPair>& pairs)
610 {
611     std::vector<int32_t> oldFileIds;
612     std::vector<int32_t> newFileIds;
613 
614     for (const auto& pair : pairs) {
615         oldFileIds.push_back(pair.first);
616         newFileIds.push_back(pair.second);
617     }
618 
619     return {std::move(oldFileIds), std::move(newFileIds)};
620 }
621 
SplitString(const std::string & str,char delimiter)622 std::vector<std::string> BackupDatabaseUtils::SplitString(const std::string& str, char delimiter)
623 {
624     std::vector<std::string> elements;
625     std::stringstream ss(str);
626     std::string item;
627     while (std::getline(ss, item, delimiter)) {
628         if (!item.empty()) {
629             elements.emplace_back(item);
630         }
631     }
632     return elements;
633 }
634 
PrintQuerySql(const std::string & querySql)635 void BackupDatabaseUtils::PrintQuerySql(const std::string& querySql)
636 {
637     MEDIA_INFO_LOG("Generated SQL Query:");
638     MEDIA_INFO_LOG("--------------------");
639     MEDIA_INFO_LOG("%{public}s", querySql.c_str());
640     MEDIA_INFO_LOG("--------------------");
641 }
642 
DeleteDuplicatePortraitAlbum(const std::vector<std::string> & albumNames,const std::vector<std::string> tagIds,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)643 bool BackupDatabaseUtils::DeleteDuplicatePortraitAlbum(const std::vector<std::string> &albumNames,
644     const std::vector<std::string> tagIds, std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
645 {
646     std::set<std::string> uniqueAlbums(albumNames.begin(), albumNames.end());
647     std::vector<std::string> uniqueAlbumNames(uniqueAlbums.begin(), uniqueAlbums.end());
648     MEDIA_INFO_LOG("unique AlbumName %{public}zu", uniqueAlbumNames.size());
649 
650     std::string inClause = BackupDatabaseUtils::JoinSQLValues<string>(uniqueAlbumNames, ", ");
651     std::string tagIdClause;
652     if (!tagIds.empty()) {
653         tagIdClause = "(" + BackupDatabaseUtils::JoinSQLValues<string>(tagIds, ", ") + ")";
654     }
655     // 删除 VisionFaceTag 表中的记录
656     std::string deleteFaceTagSql = "DELETE FROM " + VISION_FACE_TAG_TABLE +
657                                    " WHERE tag_id IN (SELECT A.tag_id FROM " + ANALYSIS_ALBUM_TABLE + " AS A, " +
658                                    VISION_FACE_TAG_TABLE + " AS B WHERE A.tag_id = B.tag_id AND " +
659                                    ANALYSIS_COL_ALBUM_NAME + " IN (" + inClause + "))";
660     ExecuteSQL(mediaLibraryRdb, deleteFaceTagSql);
661 
662     std::string imageFaceClause = "tag_id IN (SELECT A.tag_id FROM " + ANALYSIS_ALBUM_TABLE + " AS A, " +
663         VISION_IMAGE_FACE_TABLE + " AS B WHERE A.tag_id = B.tag_id AND " +
664         ANALYSIS_COL_ALBUM_NAME + " IN (" + inClause + "))";
665 
666     std::unique_ptr<NativeRdb::AbsRdbPredicates> updatePredicates =
667             make_unique<NativeRdb::AbsRdbPredicates>(VISION_IMAGE_FACE_TABLE);
668     updatePredicates->SetWhereClause(imageFaceClause);
669     int32_t deletedRows = 0;
670     NativeRdb::ValuesBucket valuesBucket;
671     valuesBucket.PutString(FACE_TAG_COL_TAG_ID, std::string("-1"));
672 
673     int32_t ret = BackupDatabaseUtils::Update(mediaLibraryRdb, deletedRows, valuesBucket, updatePredicates);
674     if (deletedRows < 0 || ret < 0) {
675         MEDIA_ERR_LOG("Failed to update tag_id colum value");
676         return false;
677     }
678 
679     /* 删除 AnalysisAlbum 表中的记录 */
680     std::string deleteAnalysisSql = "DELETE FROM " + ANALYSIS_ALBUM_TABLE +
681                                     " WHERE " + ANALYSIS_COL_ALBUM_NAME + " IN (" + inClause + ")";
682     if (!tagIds.empty()) {
683         deleteAnalysisSql += " OR ";
684         deleteAnalysisSql += "(" + ANALYSIS_COL_TAG_ID + " IN " + tagIdClause + ")";
685     }
686     ExecuteSQL(mediaLibraryRdb, deleteAnalysisSql);
687 
688     return true;
689 }
690 
ExecuteSQL(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::string & sql,const std::vector<NativeRdb::ValueObject> & args)691 int BackupDatabaseUtils::ExecuteSQL(std::shared_ptr<NativeRdb::RdbStore> rdbStore, const std::string& sql,
692     const std::vector<NativeRdb::ValueObject> &args)
693 {
694     if (rdbStore == nullptr) {
695         MEDIA_ERR_LOG("rdbStore is nullptr");
696         return E_FAIL;
697     }
698     return ExecSqlWithRetry([&]() { return rdbStore->ExecuteSql(sql, args); });
699 }
700 
BatchInsert(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & value,int64_t & rowNum)701 int32_t BackupDatabaseUtils::BatchInsert(std::shared_ptr<NativeRdb::RdbStore> rdbStore,
702     const std::string &tableName, std::vector<NativeRdb::ValuesBucket> &value, int64_t &rowNum)
703 {
704     if (rdbStore == nullptr) {
705         MEDIA_ERR_LOG("rdbStore is nullptr");
706         return E_FAIL;
707     }
708     return ExecSqlWithRetry([&]() { return rdbStore->BatchInsert(rowNum, tableName, value); });
709 }
710 
GetFileIdNewFilterClause(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,const std::vector<FileIdPair> & fileIdPair)711 std::string BackupDatabaseUtils::GetFileIdNewFilterClause(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,
712     const std::vector<FileIdPair>& fileIdPair)
713 {
714     std::vector<int32_t> result;
715     auto [oldFileIds, newFileIds] = BackupDatabaseUtils::UnzipFileIdPairs(fileIdPair);
716     std::string fileIdNewInClause = "(" + BackupDatabaseUtils::JoinValues<int>(newFileIds, ", ") + ")";
717     std::string querySql = "SELECT " + IMAGE_FACE_COL_FILE_ID +
718         " FROM " + VISION_IMAGE_FACE_TABLE +
719         " WHERE " + IMAGE_FACE_COL_FILE_ID + " IN " + fileIdNewInClause;
720 
721     auto resultSet = BackupDatabaseUtils::GetQueryResultSet(mediaLibraryRdb, querySql);
722     if (resultSet == nullptr) {
723         MEDIA_ERR_LOG("Query resultSet is null.");
724         return "()";
725     }
726 
727     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
728         int32_t value;
729         int32_t columnIndex;
730         int32_t err = resultSet->GetColumnIndex(IMAGE_FACE_COL_FILE_ID, columnIndex);
731         if (err == E_OK) {
732             resultSet->GetInt(columnIndex, value);
733             result.emplace_back(value);
734         }
735     }
736 
737     std::vector<int32_t> newFileIdsToDelete;
738     for (const auto& fileId : result) {
739         auto it = std::find_if(fileIdPair.begin(), fileIdPair.end(),
740             [fileId](const FileIdPair& pair) { return pair.second == fileId; });
741         if (it != fileIdPair.end()) {
742             newFileIdsToDelete.push_back(it->second);
743         }
744     }
745 
746     return "(" + BackupDatabaseUtils::JoinValues<int>(newFileIdsToDelete, ", ") + ")";
747 }
748 
DeleteExistingImageFaceData(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,const std::vector<FileIdPair> & fileIdPair)749 void BackupDatabaseUtils::DeleteExistingImageFaceData(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,
750     const std::vector<FileIdPair>& fileIdPair)
751 {
752     std::string fileIdNewFilterClause = GetFileIdNewFilterClause(mediaLibraryRdb, fileIdPair);
753 
754     std::string deleteFaceSql = "DELETE FROM " + VISION_IMAGE_FACE_TABLE +
755         " WHERE " + IMAGE_FACE_COL_FILE_ID + " IN " + fileIdNewFilterClause;
756     BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb, deleteFaceSql);
757 }
758 
ParseFaceTagResultSet(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,TagPairOpt & tagPair)759 void BackupDatabaseUtils::ParseFaceTagResultSet(const std::shared_ptr<NativeRdb::ResultSet>& resultSet,
760     TagPairOpt& tagPair)
761 {
762     tagPair.first = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, ANALYSIS_COL_TAG_ID);
763     tagPair.second = BackupDatabaseUtils::GetOptionalValue<std::string>(resultSet, ANALYSIS_COL_GROUP_TAG);
764 }
765 
QueryTagInfo(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)766 std::vector<TagPairOpt> BackupDatabaseUtils::QueryTagInfo(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
767 {
768     std::vector<TagPairOpt> result;
769     std::string querySql = "SELECT " + ANALYSIS_COL_TAG_ID + ", " +
770         ANALYSIS_COL_GROUP_TAG +
771         " FROM " + ANALYSIS_ALBUM_TABLE +
772         " WHERE " + ANALYSIS_COL_TAG_ID + " IS NOT NULL AND " +
773         ANALYSIS_COL_TAG_ID + " != ''";
774 
775     auto resultSet = BackupDatabaseUtils::GetQueryResultSet(mediaLibraryRdb, querySql);
776     if (resultSet == nullptr) {
777         MEDIA_ERR_LOG ("Query resultSet is null.");
778         return result;
779     }
780     while (resultSet->GoToNextRow () == NativeRdb::E_OK) {
781         TagPairOpt tagPair;
782         ParseFaceTagResultSet(resultSet, tagPair);
783         result.emplace_back(tagPair);
784     }
785     return result;
786 }
787 
UpdateGroupTagColumn(const std::vector<TagPairOpt> & updatedPairs,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)788 void BackupDatabaseUtils::UpdateGroupTagColumn(const std::vector<TagPairOpt>& updatedPairs,
789     std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
790 {
791     for (const auto& pair : updatedPairs) {
792         if (pair.first.has_value() && pair.second.has_value()) {
793             std::unique_ptr<NativeRdb::AbsRdbPredicates> predicates =
794                 std::make_unique<NativeRdb::AbsRdbPredicates>(ANALYSIS_ALBUM_TABLE);
795             std::string whereClause = ANALYSIS_COL_TAG_ID + " = '" + pair.first.value() + "'";
796             predicates->SetWhereClause(whereClause);
797 
798             int32_t updatedRows = 0;
799             NativeRdb::ValuesBucket valuesBucket;
800             valuesBucket.PutString(ANALYSIS_COL_GROUP_TAG, pair.second.value());
801 
802             int32_t ret = BackupDatabaseUtils::Update(mediaLibraryRdb, updatedRows, valuesBucket, predicates);
803             if (updatedRows <= 0 || ret < 0) {
804                 MEDIA_ERR_LOG("Failed to update group_tag for tag_id: %s", pair.first.value().c_str());
805             }
806         }
807     }
808 }
809 
UpdateFaceGroupTagsUnion(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)810 void BackupDatabaseUtils::UpdateFaceGroupTagsUnion(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
811 {
812     std::vector<TagPairOpt> tagPairs = QueryTagInfo(mediaLibraryRdb);
813     std::vector<TagPairOpt> updatedPairs;
814     std::vector<std::string> allTagIds;
815     for (const auto& pair : tagPairs) {
816         if (pair.first.has_value()) {
817             allTagIds.emplace_back(pair.first.value());
818         }
819     }
820     MEDIA_INFO_LOG("get all TagId  %{public}zu", allTagIds.size());
821     for (const auto& pair : tagPairs) {
822         if (pair.second.has_value()) {
823             std::vector<std::string> groupTags = BackupDatabaseUtils::SplitString(pair.second.value(), '|');
824             MEDIA_INFO_LOG("TagId: %{public}s, old GroupTags is: %{public}s",
825                            pair.first.value_or(std::string("-1")).c_str(), pair.second.value().c_str());
826             groupTags.erase(std::remove_if(groupTags.begin(), groupTags.end(),
827                 [&allTagIds](const std::string& tagId) {
828                 return std::find(allTagIds.begin(), allTagIds.end(), tagId) == allTagIds.end();
829                 }),
830                 groupTags.end());
831 
832             std::string newGroupTag = BackupDatabaseUtils::JoinValues<std::string>(groupTags, "|");
833             if (newGroupTag != pair.second.value()) {
834                 updatedPairs.emplace_back(pair.first, newGroupTag);
835                 MEDIA_INFO_LOG("TagId: %{public}s  GroupTags updated", pair.first.value().c_str());
836             }
837         }
838     }
839 
840     UpdateGroupTagColumn(updatedPairs, mediaLibraryRdb);
841 }
842 
UpdateTagPairs(std::vector<TagPairOpt> & updatedPairs,const std::string & newGroupTag,const std::vector<std::string> & tagIds)843 void BackupDatabaseUtils::UpdateTagPairs(std::vector<TagPairOpt>& updatedPairs, const std::string& newGroupTag,
844     const std::vector<std::string>& tagIds)
845 {
846     for (const auto& tagId : tagIds) {
847         updatedPairs.emplace_back(tagId, newGroupTag);
848     }
849 }
850 
UpdateGroupTags(std::vector<TagPairOpt> & updatedPairs,const std::unordered_map<std::string,std::vector<std::string>> & groupTagMap)851 void BackupDatabaseUtils::UpdateGroupTags(std::vector<TagPairOpt>& updatedPairs,
852     const std::unordered_map<std::string, std::vector<std::string>>& groupTagMap)
853 {
854     for (auto &[groupTag, tagIds] : groupTagMap) {
855         if (tagIds.empty()) {
856             continue;
857         }
858 
859         const std::string newGroupTag =
860             (tagIds.size() > 1) ? BackupDatabaseUtils::JoinValues(tagIds, "|") : tagIds.front();
861         if (newGroupTag != groupTag) {
862             UpdateTagPairs(updatedPairs, newGroupTag, tagIds);
863         }
864     }
865 }
866 
867     /* 双框架的 group_id 是合并相册之一的某一 tag_id */
UpdateFaceGroupTagOfDualFrame(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)868 void BackupDatabaseUtils::UpdateFaceGroupTagOfDualFrame(std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb)
869 {
870     std::vector<TagPairOpt> tagPairs = QueryTagInfo(mediaLibraryRdb);
871     std::vector<TagPairOpt> updatedPairs;
872     std::unordered_map<std::string, std::vector<std::string>> groupTagMap;
873 
874     for (const auto& pair : tagPairs) {
875         if (pair.first.has_value() && pair.second.has_value()) {
876             groupTagMap[pair.second.value()].push_back(pair.first.value());
877         } else {
878             MEDIA_INFO_LOG("Found tag_id without group_tag: %{public}s", pair.first.value().c_str());
879         }
880     }
881 
882     UpdateGroupTags(updatedPairs, groupTagMap);
883     UpdateGroupTagColumn(updatedPairs, mediaLibraryRdb);
884 }
885 
UpdateAssociateFileId(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::vector<FileInfo> & fileInfos)886 void BackupDatabaseUtils::UpdateAssociateFileId(std::shared_ptr<NativeRdb::RdbStore> rdbStore,
887     const std::vector<FileInfo> &fileInfos)
888 {
889     for (const FileInfo &fileInfo : fileInfos) {
890         if (fileInfo.associateFileId <= 0 || fileInfo.fileIdOld <= 0 || fileInfo.fileIdNew <= 0) {
891             continue;
892         }
893         int32_t updateAssociateId = -1;
894         bool ret = fileIdOld2NewForCloudEnhancement.Find(fileInfo.associateFileId, updateAssociateId);
895         if (!ret) {
896             fileIdOld2NewForCloudEnhancement.Insert(fileInfo.fileIdOld, fileInfo.fileIdNew);
897             continue;
898         }
899         int32_t changeRows = 0;
900         NativeRdb::ValuesBucket updatePostBucket;
901         updatePostBucket.Put(PhotoColumn::PHOTO_ASSOCIATE_FILE_ID, updateAssociateId);
902         std::unique_ptr<NativeRdb::AbsRdbPredicates> predicates =
903             make_unique<NativeRdb::AbsRdbPredicates>(PhotoColumn::PHOTOS_TABLE);
904         predicates->SetWhereClause("file_id=?");
905         predicates->SetWhereArgs({ to_string(fileInfo.fileIdNew) });
906         BackupDatabaseUtils::Update(rdbStore, changeRows, updatePostBucket, predicates);
907         if (changeRows > 0) {
908             MEDIA_INFO_LOG("update, old:%{public}d, new:%{public}d, old_associate:%{public}d, new_associate:%{public}d",
909                 fileInfo.fileIdOld, fileInfo.fileIdNew, fileInfo.associateFileId, updateAssociateId);
910         }
911 
912         NativeRdb::ValuesBucket updatePreBucket;
913         updatePreBucket.Put(PhotoColumn::PHOTO_ASSOCIATE_FILE_ID, fileInfo.fileIdNew);
914         predicates->SetWhereArgs({ to_string(updateAssociateId) });
915         BackupDatabaseUtils::Update(rdbStore, changeRows, updatePreBucket, predicates);
916         if (changeRows > 0) {
917             MEDIA_INFO_LOG("update, old:%{public}d, new:%{public}d, new_associate:%{public}d",
918                 fileInfo.associateFileId, updateAssociateId, fileInfo.fileIdNew);
919         }
920         fileIdOld2NewForCloudEnhancement.Erase(fileInfo.associateFileId);
921     }
922 }
923 
CheckDbIntegrity(std::shared_ptr<NativeRdb::RdbStore> rdbStore,int32_t sceneCode,const std::string & dbTag)924 std::string BackupDatabaseUtils::CheckDbIntegrity(std::shared_ptr<NativeRdb::RdbStore> rdbStore, int32_t sceneCode,
925     const std::string &dbTag)
926 {
927     const std::string querySql = "PRAGMA " + COLUMN_INTEGRITY_CHECK;
928     auto resultSet = GetQueryResultSet(rdbStore, querySql);
929     if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
930         MEDIA_ERR_LOG ("Query resultSet is null or GoToFirstRow failed.");
931         return "";
932     }
933     std::string result = GetStringVal(COLUMN_INTEGRITY_CHECK, resultSet);
934     MEDIA_INFO_LOG("Check db integrity: %{public}d, %{public}s, %{public}s", sceneCode, dbTag.c_str(), result.c_str());
935     return result;
936 }
937 
QueryMediaTypeCount(const std::shared_ptr<NativeRdb::RdbStore> & rdbStore,const std::string & querySql)938 std::unordered_map<int32_t, int32_t> BackupDatabaseUtils::QueryMediaTypeCount(
939     const std::shared_ptr<NativeRdb::RdbStore>& rdbStore, const std::string& querySql)
940 {
941     std::unordered_map<int32_t, int32_t> mediaTypeCountMap;
942     auto resultSet = GetQueryResultSet(rdbStore, querySql);
943     if (resultSet == nullptr) {
944         MEDIA_ERR_LOG("resultSet is nullptr");
945         return mediaTypeCountMap;
946     }
947     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
948         int32_t mediaType = GetInt32Val(EXTERNAL_MEDIA_TYPE, resultSet);
949         int32_t count = GetInt32Val(CUSTOM_COUNT, resultSet);
950         mediaTypeCountMap[mediaType] = count;
951     }
952     return mediaTypeCountMap;
953 }
954 } // namespace Media
955 } // namespace OHOS