1 /*
2  * Copyright (C) 2023 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 #include "medialibrary_rdb_transaction.h"
17 #include "medialibrary_restore.h"
18 #include "media_file_utils.h"
19 #include "media_log.h"
20 #include "photo_album_column.h"
21 #include "photo_map_column.h"
22 #include "medialibrary_rdbstore.h"
23 #include "cloud_sync_helper.h"
24 
25 namespace OHOS::Media {
26 using namespace std;
27 using namespace OHOS::NativeRdb;
28 constexpr int32_t E_HAS_DB_ERROR = -222;
29 constexpr int32_t E_OK = 0;
30 constexpr int32_t RETRY_TRANS_MAX_TIMES = 2;
31 constexpr int32_t RETRY_TRANS_MAX_TIMES_FOR_BACKUP = 10;
32 
TransactionOperations(std::string funcName)33 TransactionOperations::TransactionOperations(std::string funcName)
34     : funcName_(funcName), reporter_(funcName)
35 {}
36 
~TransactionOperations()37 TransactionOperations::~TransactionOperations()
38 {
39     if (transaction_ == nullptr) {
40         return;
41     }
42     Rollback();
43 }
44 
SetBackupRdbStore(std::shared_ptr<OHOS::NativeRdb::RdbStore> rdbStore)45 void TransactionOperations::SetBackupRdbStore(std::shared_ptr<OHOS::NativeRdb::RdbStore> rdbStore)
46 {
47     backupRdbStore_ = rdbStore;
48 }
49 
Start(bool isBackup)50 int32_t TransactionOperations::Start(bool isBackup)
51 {
52     MEDIA_INFO_LOG("Start transaction_, funName is :%{public}s", funcName_.c_str());
53     if (isBackup) {
54         rdbStore_ = backupRdbStore_;
55     } else {
56         rdbStore_ = MediaLibraryRdbStore::GetRaw();
57     }
58     if (rdbStore_ == nullptr) {
59         reporter_.ReportError(DfxTransaction::AbnormalType::NULLPTR_ERROR, E_HAS_DB_ERROR);
60         MEDIA_ERR_LOG("rdbStore_ is null, isBackup = %{public}d", isBackup);
61         return E_HAS_DB_ERROR;
62     }
63 
64     int currentTime = 0;
65     int errCode = -1;
66     while (currentTime <= MAX_TRY_TIMES) {
67         auto [ret, transaction] = rdbStore_->CreateTransaction(OHOS::NativeRdb::Transaction::DEFERRED);
68         errCode = ret;
69         if (ret == NativeRdb::E_OK) {
70             transaction_ = transaction;
71             break;
72         } else if (ret == NativeRdb::E_SQLITE_LOCKED || ret == NativeRdb::E_DATABASE_BUSY) {
73             this_thread::sleep_for(chrono::milliseconds(TRANSACTION_WAIT_INTERVAL));
74             currentTime++;
75             MEDIA_ERR_LOG("CreateTransaction busy, ret:%{public}d, time:%{public}d", ret, currentTime);
76         } else {
77             MEDIA_ERR_LOG("CreateTransaction faile, ret = %{public}d", ret);
78             break;
79         }
80     }
81     if (errCode != NativeRdb::E_OK) {
82         reporter_.ReportError(DfxTransaction::AbnormalType::CREATE_ERROR, errCode);
83         errCode = E_HAS_DB_ERROR;
84     }
85     return errCode;
86 }
87 
Commit()88 int32_t TransactionOperations::Commit()
89 {
90     if (transaction_ == nullptr) {
91         reporter_.ReportError(DfxTransaction::AbnormalType::NULLPTR_ERROR, E_HAS_DB_ERROR);
92         MEDIA_ERR_LOG("transaction is null");
93         return E_HAS_DB_ERROR;
94     }
95     MEDIA_INFO_LOG("Commit transaction, funcName is :%{public}s", funcName_.c_str());
96     auto ret = transaction_->Commit();
97     if (ret != NativeRdb::E_OK) {
98         reporter_.ReportError(DfxTransaction::AbnormalType::COMMIT_ERROR, ret);
99         MEDIA_ERR_LOG("transaction commit fail!, ret:%{public}d", ret);
100     } else {
101         reporter_.ReportIfTimeout();
102     }
103     return ret;
104 }
105 
Finish()106 int32_t TransactionOperations::Finish()
107 {
108     if (transaction_ == nullptr) {
109         reporter_.ReportError(DfxTransaction::AbnormalType::NULLPTR_ERROR, E_HAS_DB_ERROR);
110         MEDIA_ERR_LOG("transaction is null");
111         return E_HAS_DB_ERROR;
112     }
113     MEDIA_INFO_LOG("Commit transaction, funcName is :%{public}s", funcName_.c_str());
114     auto ret = transaction_->Commit();
115     transaction_ = nullptr;
116     if (ret != NativeRdb::E_OK) {
117         reporter_.ReportError(DfxTransaction::AbnormalType::COMMIT_ERROR, ret);
118         MEDIA_ERR_LOG("transaction commit fail!, ret:%{public}d", ret);
119     } else {
120         reporter_.ReportIfTimeout();
121     }
122 #ifdef CLOUD_SYNC_MANAGER
123     if (isSkipCloudSync_) {
124         MEDIA_INFO_LOG("recover cloud sync for commit");
125         CloudSyncHelper::GetInstance()->StartSync();
126         isSkipCloudSync_ = false;
127     }
128 #endif
129     return ret;
130 }
131 
TryTrans(std::function<int (void)> & func,bool isBackup)132 int32_t TransactionOperations::TryTrans(std::function<int(void)> &func, bool isBackup)
133 {
134     int32_t err = NativeRdb::E_OK;
135     err = Start(isBackup);
136     if (err != NativeRdb::E_OK) {
137         MEDIA_ERR_LOG("Failed to begin transaction, err: %{public}d", err);
138         return err;
139     }
140     err = func();
141     if (err != E_OK) {
142         MEDIA_ERR_LOG("TryTrans: trans function fail!, ret:%{public}d", err);
143         Rollback();
144         return err;
145     }
146     err = Finish();
147     if (err != E_OK) {
148         MEDIA_ERR_LOG("TryTrans: trans finish fail!, ret:%{public}d", err);
149     }
150     return err;
151 }
152 
RetryTrans(std::function<int (void)> & func,bool isBackup)153 int32_t TransactionOperations::RetryTrans(std::function<int(void)> &func, bool isBackup)
154 {
155     int32_t currentTime = 0;
156     int maxTryTimes = isBackup ? RETRY_TRANS_MAX_TIMES_FOR_BACKUP : RETRY_TRANS_MAX_TIMES;
157     int32_t err = NativeRdb::E_OK;
158     while (currentTime < maxTryTimes) {
159         err = TryTrans(func, isBackup);
160         if (err == E_OK) {
161             return err;
162         }
163         if (err == NativeRdb::E_SQLITE_BUSY && !isSkipCloudSync_) {
164             MEDIA_ERR_LOG("TryTrans busy, err:%{public}d", err);
165 #ifdef CLOUD_SYNC_MANAGER
166             MEDIA_INFO_LOG("Stop cloud sync");
167             FileManagement::CloudSync::CloudSyncManager::GetInstance()
168                 .StopSync("com.ohos.medialibrary.medialibrarydata");
169             isSkipCloudSync_ = true;
170 #endif
171         }
172         currentTime++;
173         reporter_.Restart();
174     }
175     MEDIA_INFO_LOG("RetryTrans result is :%{public}d", err);
176     return err;
177 }
178 
Rollback()179 int32_t TransactionOperations::Rollback()
180 {
181     if (transaction_ == nullptr) {
182         MEDIA_ERR_LOG("transaction_ is null");
183         return NativeRdb::E_OK;
184     }
185     auto ret = transaction_->Rollback();
186     transaction_ = nullptr;
187     if (ret != NativeRdb::E_OK) {
188         reporter_.ReportError(DfxTransaction::AbnormalType::ROLLBACK_ERROR, ret);
189         MEDIA_ERR_LOG("Rollback fail:%{public}d", ret);
190     }
191 #ifdef CLOUD_SYNC_MANAGER
192     if (isSkipCloudSync_) {
193         MEDIA_INFO_LOG("recover cloud sync for rollback");
194         CloudSyncHelper::GetInstance()->StartSync();
195         isSkipCloudSync_ = false;
196     }
197 #endif
198     return ret;
199 }
200 
ExecuteSql(const std::string & sql,const std::vector<NativeRdb::ValueObject> & args)201 int32_t TransactionOperations::ExecuteSql(const std::string &sql, const std::vector<NativeRdb::ValueObject> &args)
202 {
203     if (transaction_ == nullptr) {
204         MEDIA_ERR_LOG("transaction is null");
205         return E_HAS_DB_ERROR;
206     }
207     auto [ret, value] = transaction_->Execute(sql, args);
208     if (ret != NativeRdb::E_OK) {
209         MEDIA_ERR_LOG("rdbStore_->ExecuteSql failed, ret = %{public}d", ret);
210         MediaLibraryRestore::GetInstance().CheckRestore(ret);
211         return E_HAS_DB_ERROR;
212     }
213     return ret;
214 }
215 
Execute(const std::string & sql,const std::vector<NativeRdb::ValueObject> & args)216 int32_t TransactionOperations::Execute(const std::string &sql, const std::vector<NativeRdb::ValueObject> &args)
217 {
218     if (transaction_ == nullptr) {
219         MEDIA_ERR_LOG("transaction is null");
220         return E_HAS_DB_ERROR;
221     }
222     auto [ret, value] = transaction_->Execute(sql, args);
223     if (ret != NativeRdb::E_OK) {
224         MEDIA_ERR_LOG("rdbStore_->Execute failed, ret = %{public}d", ret);
225         MediaLibraryRestore::GetInstance().CheckRestore(ret);
226         return E_HAS_DB_ERROR;
227     }
228     return ret;
229 }
230 
ExecuteForLastInsertedRowId(const std::string & sql,const std::vector<NativeRdb::ValueObject> & bindArgs)231 int32_t TransactionOperations::ExecuteForLastInsertedRowId(const std::string &sql,
232     const std::vector<NativeRdb::ValueObject> &bindArgs)
233 {
234     if (transaction_ == nullptr) {
235         MEDIA_ERR_LOG("transaction is null");
236         return E_HAS_DB_ERROR;
237     }
238     int64_t lastInsertRowId = 0;
239     auto [err, valueObject] = transaction_->Execute(sql, bindArgs);
240     (void)valueObject.GetLong(lastInsertRowId);
241     if (err != E_OK) {
242         MEDIA_ERR_LOG("Failed to execute insert, err: %{public}d", err);
243         MediaLibraryRestore::GetInstance().CheckRestore(err);
244         return E_HAS_DB_ERROR;
245     }
246     return lastInsertRowId;
247 }
248 
Insert(MediaLibraryCommand & cmd,int64_t & rowId)249 int32_t TransactionOperations::Insert(MediaLibraryCommand &cmd, int64_t &rowId)
250 {
251     if (transaction_ == nullptr) {
252         MEDIA_ERR_LOG("transaction is null");
253         return E_HAS_DB_ERROR;
254     }
255 
256     auto [ret, rows] = transaction_->Insert(cmd.GetTableName(), cmd.GetValueBucket());
257     rowId = rows;
258     if (ret != NativeRdb::E_OK) {
259         MEDIA_ERR_LOG("rdbStore_->Insert failed, ret = %{public}d", ret);
260         MediaLibraryRestore::GetInstance().CheckRestore(ret);
261         return E_HAS_DB_ERROR;
262     }
263 
264     MEDIA_DEBUG_LOG("rdbStore_->Insert end, rowId = %d, ret = %{public}d", (int)rowId, ret);
265     return ret;
266 }
267 
Update(NativeRdb::ValuesBucket & values,const NativeRdb::AbsRdbPredicates & predicates)268 int32_t TransactionOperations::Update(NativeRdb::ValuesBucket &values, const NativeRdb::AbsRdbPredicates &predicates)
269 {
270     if (transaction_ == nullptr) {
271         MEDIA_ERR_LOG("transaction is null");
272         return E_HAS_DB_ERROR;
273     }
274     if (predicates.GetTableName() == PhotoColumn::PHOTOS_TABLE) {
275         values.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, MediaFileUtils::UTCTimeMilliSeconds());
276         values.PutLong(PhotoColumn::PHOTO_LAST_VISIT_TIME, MediaFileUtils::UTCTimeMilliSeconds());
277     }
278     auto [err, changedRows] = transaction_->Update(values, predicates);
279     if (err != E_OK) {
280         MEDIA_ERR_LOG("Failed to execute update, err: %{public}d", err);
281         MediaLibraryRestore::GetInstance().CheckRestore(err);
282         return E_HAS_DB_ERROR;
283     }
284     return changedRows;
285 }
286 
Update(int32_t & changedRows,NativeRdb::ValuesBucket & values,const NativeRdb::AbsRdbPredicates & predicates)287 int32_t TransactionOperations::Update(int32_t &changedRows, NativeRdb::ValuesBucket &values,
288     const NativeRdb::AbsRdbPredicates &predicates)
289 {
290     if (transaction_ == nullptr) {
291         MEDIA_ERR_LOG("transaction is null");
292         return E_HAS_DB_ERROR;
293     }
294     auto [err, rows] = transaction_->Update(values, predicates);
295     changedRows = rows;
296     if (err != E_OK) {
297         MEDIA_ERR_LOG("Failed to execute update, err: %{public}d", err);
298         MediaLibraryRestore::GetInstance().CheckRestore(err);
299         return err;
300     }
301     return err;
302 }
303 
Update(MediaLibraryCommand & cmd,int32_t & changedRows)304 int32_t TransactionOperations::Update(MediaLibraryCommand &cmd, int32_t &changedRows)
305 {
306     if (transaction_ == nullptr) {
307         MEDIA_ERR_LOG("transaction_ is null");
308         return E_HAS_DB_ERROR;
309     }
310 
311     if (cmd.GetTableName() == PhotoColumn::PHOTOS_TABLE) {
312         cmd.GetValueBucket().PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED,
313             MediaFileUtils::UTCTimeMilliSeconds());
314         cmd.GetValueBucket().PutLong(PhotoColumn::PHOTO_LAST_VISIT_TIME,
315             MediaFileUtils::UTCTimeMilliSeconds());
316     }
317 
318     int32_t ret = E_HAS_DB_ERROR;
319     auto res = transaction_->Update(cmd.GetTableName(), cmd.GetValueBucket(),
320         cmd.GetAbsRdbPredicates()->GetWhereClause(), cmd.GetAbsRdbPredicates()->GetBindArgs());
321     ret = res.first;
322     changedRows = res.second;
323 
324     if (ret != NativeRdb::E_OK) {
325         MEDIA_ERR_LOG("rdbStore_->Update failed, ret = %{public}d", ret);
326         MediaLibraryRestore::GetInstance().CheckRestore(ret);
327         return E_HAS_DB_ERROR;
328     }
329     return ret;
330 }
331 
BatchInsert(int64_t & outRowId,const std::string & table,const std::vector<NativeRdb::ValuesBucket> & values)332 int32_t TransactionOperations::BatchInsert(int64_t &outRowId, const std::string &table,
333     const std::vector<NativeRdb::ValuesBucket> &values)
334 {
335     if (transaction_ == nullptr) {
336         MEDIA_ERR_LOG("transaction_ is null");
337         return E_HAS_DB_ERROR;
338     }
339 
340     auto [ret, rows] = transaction_->BatchInsert(table, values);
341     outRowId = rows;
342     if (ret != NativeRdb::E_OK) {
343         MEDIA_ERR_LOG("transaction_->BatchInsert failed, ret = %{public}d", ret);
344         MediaLibraryRestore::GetInstance().CheckRestore(ret);
345         return E_HAS_DB_ERROR;
346     }
347     MEDIA_DEBUG_LOG("transaction_->BatchInsert end, rowId = %d, ret = %{public}d", (int)outRowId, ret);
348     return ret;
349 }
350 
BatchInsert(MediaLibraryCommand & cmd,int64_t & outInsertNum,const std::vector<ValuesBucket> & values)351 int32_t TransactionOperations::BatchInsert(MediaLibraryCommand &cmd, int64_t& outInsertNum,
352     const std::vector<ValuesBucket>& values)
353 {
354     if (transaction_ == nullptr) {
355         MEDIA_ERR_LOG("transaction_ is null");
356         return E_HAS_DB_ERROR;
357     }
358     auto [ret, rows] = transaction_->BatchInsert(cmd.GetTableName(), values);
359     outInsertNum = rows;
360     if (ret != NativeRdb::E_OK) {
361         MEDIA_ERR_LOG("transaction_->BatchInsert failed, ret = %{public}d", ret);
362         MediaLibraryRestore::GetInstance().CheckRestore(ret);
363         return E_HAS_DB_ERROR;
364     }
365     MEDIA_DEBUG_LOG("rdbStore_->BatchInsert end, rowId = %d, ret = %{public}d", (int)outInsertNum, ret);
366     return ret;
367 }
368 
Insert(int64_t & rowId,const std::string tableName,const NativeRdb::ValuesBucket & values)369 int32_t TransactionOperations::Insert(int64_t &rowId, const std::string tableName,
370     const NativeRdb::ValuesBucket &values)
371 {
372     if (transaction_ == nullptr) {
373         MEDIA_ERR_LOG("transaction_ is null");
374         return E_HAS_DB_ERROR;
375     }
376 
377     auto [ret, rows] = transaction_->Insert(tableName, values);
378     rowId = rows;
379     if (ret != NativeRdb::E_OK) {
380         MEDIA_ERR_LOG("transaction_->Insert failed, ret = %{public}d", ret);
381         MediaLibraryRestore::GetInstance().CheckRestore(ret);
382         return E_HAS_DB_ERROR;
383     }
384 
385     MEDIA_DEBUG_LOG("transaction_->Insert end, rowId = %d, ret = %{public}d", (int)rowId, ret);
386     return ret;
387 }
388 
DoDeleteFromPredicates(const AbsRdbPredicates & predicates,int32_t & deletedRows,std::shared_ptr<OHOS::NativeRdb::Transaction> transaction)389 static int32_t DoDeleteFromPredicates(const AbsRdbPredicates &predicates,
390     int32_t &deletedRows, std::shared_ptr<OHOS::NativeRdb::Transaction> transaction)
391 {
392     int32_t ret = NativeRdb::E_ERROR;
393     string tableName = predicates.GetTableName();
394     ValuesBucket valuesBucket;
395     if (tableName == MEDIALIBRARY_TABLE || tableName == PhotoColumn::PHOTOS_TABLE) {
396         valuesBucket.PutInt(MEDIA_DATA_DB_DIRTY, static_cast<int32_t>(DirtyType::TYPE_DELETED));
397         valuesBucket.PutInt(MEDIA_DATA_DB_SYNC_STATUS, static_cast<int32_t>(SyncStatusType::TYPE_UPLOAD));
398         valuesBucket.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, MediaFileUtils::UTCTimeMilliSeconds());
399         auto res = transaction->Update(valuesBucket, predicates);
400         ret = res.first;
401         deletedRows = res.second;
402         MEDIA_INFO_LOG("delete photos permanently, ret: %{public}d", ret);
403     } else if (tableName == PhotoAlbumColumns::TABLE) {
404         valuesBucket.PutInt(PhotoAlbumColumns::ALBUM_DIRTY, static_cast<int32_t>(DirtyType::TYPE_DELETED));
405         auto res = transaction->Update(valuesBucket, predicates);
406         ret = res.first;
407         deletedRows = res.second;
408     } else if (tableName == PhotoMap::TABLE) {
409         valuesBucket.PutInt(PhotoMap::DIRTY, static_cast<int32_t>(DirtyType::TYPE_DELETED));
410         auto res = transaction->Update(valuesBucket, predicates);
411         ret = res.first;
412         deletedRows = res.second;
413     } else {
414         auto res = transaction->Delete(predicates);
415         ret = res.first;
416         deletedRows = res.second;
417     }
418     return ret;
419 }
420 
Delete(MediaLibraryCommand & cmd,int32_t & deletedRows)421 int32_t TransactionOperations::Delete(MediaLibraryCommand &cmd, int32_t &deletedRows)
422 {
423     if (transaction_ == nullptr) {
424         MEDIA_ERR_LOG("transaction_ is null");
425         return E_HAS_DB_ERROR;
426     }
427     /* local delete */
428     int32_t ret = DoDeleteFromPredicates(*(cmd.GetAbsRdbPredicates()), deletedRows, transaction_);
429     if (ret != NativeRdb::E_OK) {
430         MEDIA_ERR_LOG("rdbStore_->Delete failed, ret = %{public}d", ret);
431         MediaLibraryRestore::GetInstance().CheckRestore(ret);
432         return E_HAS_DB_ERROR;
433     }
434     return ret;
435 }
436 } // namespace OHOS::Media
437