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