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