1 /*
2 * Copyright (c) 2022 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 "store_session.h"
17 #include <chrono>
18 #include <stack>
19 #include <thread>
20 #include "logger.h"
21 #include "rdb_errno.h"
22 #include "shared_block.h"
23 #include "sqlite_database_utils.h"
24 #include "sqlite_utils.h"
25 #include "base_transaction.h"
26
27 namespace OHOS::NativeRdb {
StoreSession(SqliteConnectionPool & connectionPool)28 StoreSession::StoreSession(SqliteConnectionPool &connectionPool)
29 : connectionPool(connectionPool), connection(nullptr), connectionUseCount(0), isInStepQuery(false)
30 {
31 }
32
~StoreSession()33 StoreSession::~StoreSession()
34 {
35 }
36
AcquireConnection(bool isReadOnly)37 void StoreSession::AcquireConnection(bool isReadOnly)
38 {
39 if (connection == nullptr) {
40 connection = connectionPool.AcquireConnection(isReadOnly);
41 }
42
43 connectionUseCount += 1;
44 }
45
ReleaseConnection()46 void StoreSession::ReleaseConnection()
47 {
48 if ((connection == nullptr) || (connectionUseCount <= 0)) {
49 LOG_ERROR("SQLiteSession ReleaseConnection repeated release");
50 return;
51 }
52
53 if (--connectionUseCount == 0) {
54 connectionPool.ReleaseConnection(connection);
55 connection = nullptr;
56 }
57 }
58
PrepareAndGetInfo(const std::string & sql,bool & outIsReadOnly,int & numParameters,std::vector<std::string> & columnNames)59 int StoreSession::PrepareAndGetInfo(
60 const std::string &sql, bool &outIsReadOnly, int &numParameters, std::vector<std::string> &columnNames)
61 {
62 // Obtains the type of SQL statement.
63 int type = SqliteUtils::GetSqlStatementType(sql);
64 if (SqliteUtils::IsSpecial(type)) {
65 return E_TRANSACTION_IN_EXECUTE;
66 }
67 bool assumeReadOnly = SqliteUtils::IsSqlReadOnly(type);
68
69 AcquireConnection(assumeReadOnly);
70 int errCode = connection->PrepareAndGetInfo(sql, outIsReadOnly, numParameters, columnNames);
71 if (errCode != 0) {
72 ReleaseConnection();
73 return errCode;
74 }
75
76 ReleaseConnection();
77 return E_OK;
78 }
79
BeginExecuteSql(const std::string & sql)80 int StoreSession::BeginExecuteSql(const std::string &sql)
81 {
82 int type = SqliteUtils::GetSqlStatementType(sql);
83 if (SqliteUtils::IsSpecial(type)) {
84 return E_TRANSACTION_IN_EXECUTE;
85 }
86
87 bool assumeReadOnly = SqliteUtils::IsSqlReadOnly(type);
88 bool isReadOnly = false;
89 AcquireConnection(assumeReadOnly);
90 int errCode = connection->Prepare(sql, isReadOnly);
91 if (errCode != 0) {
92 ReleaseConnection();
93 return errCode;
94 }
95
96 if (isReadOnly == connection->IsWriteConnection()) {
97 ReleaseConnection();
98 AcquireConnection(isReadOnly);
99 if (!isReadOnly && !connection->IsWriteConnection()) {
100 LOG_ERROR("StoreSession BeginExecutea : read connection can not execute write operation");
101 ReleaseConnection();
102 return E_EXECUTE_WRITE_IN_READ_CONNECTION;
103 }
104 }
105
106 return E_OK;
107 }
ExecuteSql(const std::string & sql,const std::vector<ValueObject> & bindArgs)108 int StoreSession::ExecuteSql(const std::string &sql, const std::vector<ValueObject> &bindArgs)
109 {
110 int errCode = BeginExecuteSql(sql);
111 if (errCode != 0) {
112 return errCode;
113 }
114
115 errCode = connection->ExecuteSql(sql, bindArgs);
116 ReleaseConnection();
117 return errCode;
118 }
119
ExecuteForChangedRowCount(int & changedRows,const std::string & sql,const std::vector<ValueObject> & bindArgs)120 int StoreSession::ExecuteForChangedRowCount(
121 int &changedRows, const std::string &sql, const std::vector<ValueObject> &bindArgs)
122 {
123 int errCode = BeginExecuteSql(sql);
124 if (errCode != 0) {
125 return errCode;
126 }
127
128 errCode = connection->ExecuteForChangedRowCount(changedRows, sql, bindArgs);
129 ReleaseConnection();
130 return errCode;
131 }
132
ExecuteForLastInsertedRowId(int64_t & outRowId,const std::string & sql,const std::vector<ValueObject> & bindArgs)133 int StoreSession::ExecuteForLastInsertedRowId(
134 int64_t &outRowId, const std::string &sql, const std::vector<ValueObject> &bindArgs)
135 {
136 int errCode = BeginExecuteSql(sql);
137 if (errCode != 0) {
138 LOG_ERROR("rdbStore BeginExecuteSql failed");
139 return errCode;
140 }
141
142 errCode = connection->ExecuteForLastInsertedRowId(outRowId, sql, bindArgs);
143 if (errCode != E_OK) {
144 LOG_ERROR("rdbStore ExecuteForLastInsertedRowId FAILED");
145 }
146 ReleaseConnection();
147 return errCode;
148 }
149
ExecuteGetLong(int64_t & outValue,const std::string & sql,const std::vector<ValueObject> & bindArgs)150 int StoreSession::ExecuteGetLong(int64_t &outValue, const std::string &sql, const std::vector<ValueObject> &bindArgs)
151 {
152 int errCode = BeginExecuteSql(sql);
153 if (errCode != 0) {
154 return errCode;
155 }
156
157 errCode = connection->ExecuteGetLong(outValue, sql, bindArgs);
158 ReleaseConnection();
159 return errCode;
160 }
161
ExecuteGetString(std::string & outValue,const std::string & sql,const std::vector<ValueObject> & bindArgs)162 int StoreSession::ExecuteGetString(
163 std::string &outValue, const std::string &sql, const std::vector<ValueObject> &bindArgs)
164 {
165 int errCode = BeginExecuteSql(sql);
166 if (errCode != 0) {
167 return errCode;
168 }
169 std::string sqlstr = sql;
170 int type = SqliteDatabaseUtils::GetSqlStatementType(sqlstr);
171 if (type == STATEMENT_PRAGMA) {
172 ReleaseConnection();
173 AcquireConnection(false);
174 }
175 errCode = connection->ExecuteGetString(outValue, sql, bindArgs);
176 ReleaseConnection();
177 return errCode;
178 }
179
Backup(const std::string databasePath,const std::vector<uint8_t> destEncryptKey)180 int StoreSession::Backup(const std::string databasePath, const std::vector<uint8_t> destEncryptKey)
181 {
182 std::vector<ValueObject> bindArgs;
183 bindArgs.push_back(ValueObject(databasePath));
184 if (destEncryptKey.size() != 0) {
185 bindArgs.push_back(ValueObject(destEncryptKey));
186 } else {
187 std::string str = "";
188 bindArgs.push_back(ValueObject(str));
189 }
190
191 int errCode = ExecuteSql(ATTACH_BACKUP_SQL, bindArgs);
192 if (errCode != E_OK) {
193 LOG_ERROR("ExecuteSql ATTACH_BACKUP_SQL error %{public}d", errCode);
194 return errCode;
195 }
196 int64_t count;
197 errCode = ExecuteGetLong(count, EXPORT_SQL, std::vector<ValueObject>());
198 if (errCode != E_OK) {
199 LOG_ERROR("ExecuteSql EXPORT_SQL error %{public}d", errCode);
200 return errCode;
201 }
202
203 errCode = ExecuteSql(DETACH_BACKUP_SQL, std::vector<ValueObject>());
204 if (errCode != E_OK) {
205 LOG_ERROR("ExecuteSql DETACH_BACKUP_SQL error %{public}d", errCode);
206 return errCode;
207 }
208 return E_OK;
209 }
210
211 // Checks whether this thread holds a database connection.
IsHoldingConnection() const212 bool StoreSession::IsHoldingConnection() const
213 {
214 if (connection == nullptr) {
215 return false;
216 } else {
217 return true;
218 }
219 }
220
CheckNoTransaction() const221 int StoreSession::CheckNoTransaction() const
222 {
223 int errorCode = 0;
224 if (connectionPool.getTransactionStack().empty()) {
225 errorCode = E_STORE_SESSION_NO_CURRENT_TRANSACTION;
226 return errorCode;
227 }
228 return E_OK;
229 }
230
GiveConnectionTemporarily(long milliseconds)231 int StoreSession::GiveConnectionTemporarily(long milliseconds)
232 {
233 int errorCode = CheckNoTransaction();
234 if (errorCode != E_OK) {
235 return errorCode;
236 }
237 BaseTransaction transaction = connectionPool.getTransactionStack().top();
238 if (transaction.IsMarkedSuccessful() || connectionPool.getTransactionStack().size() > 1) {
239 errorCode = E_STORE_SESSION_NOT_GIVE_CONNECTION_TEMPORARILY;
240 return errorCode;
241 }
242
243 MarkAsCommit();
244 EndTransaction();
245 if (milliseconds > 0) {
246 std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
247 }
248 BeginTransaction();
249 return E_OK;
250 }
251
Attach(const std::string & alias,const std::string & pathName,const std::vector<uint8_t> destEncryptKey)252 int StoreSession::Attach(
253 const std::string &alias, const std::string &pathName, const std::vector<uint8_t> destEncryptKey)
254 {
255 std::string journalMode;
256 int errCode = ExecuteGetString(journalMode, "PRAGMA journal_mode", std::vector<ValueObject>());
257 if (errCode != E_OK) {
258 LOG_ERROR("RdbStoreImpl CheckAttach fail to get journal mode : %{public}d", errCode);
259 return errCode;
260 }
261 journalMode = SqliteUtils::StrToUpper(journalMode);
262 if (journalMode == "WAL") {
263 LOG_ERROR("RdbStoreImpl attach is not supported in WAL mode");
264 return E_NOT_SUPPORTED_ATTACH_IN_WAL_MODE;
265 }
266
267 std::vector<ValueObject> bindArgs;
268 bindArgs.push_back(ValueObject(pathName));
269 bindArgs.push_back(ValueObject(alias));
270 if (destEncryptKey.size() != 0) {
271 bindArgs.push_back(ValueObject(destEncryptKey));
272 } else {
273 std::string str = "";
274 bindArgs.push_back(ValueObject(str));
275 }
276 errCode = ExecuteSql(ATTACH_SQL, bindArgs);
277 if (errCode != E_OK) {
278 LOG_ERROR("ExecuteSql ATTACH_SQL error %{public}d", errCode);
279 return errCode;
280 }
281
282 return E_OK;
283 }
284
BeginTransaction(TransactionObserver * transactionObserver)285 int StoreSession::BeginTransaction(TransactionObserver *transactionObserver)
286 {
287 if (connectionPool.getTransactionStack().empty()) {
288 AcquireConnection(false);
289 if (!connection->IsWriteConnection()) {
290 LOG_ERROR("StoreSession BeginExecutea : read connection can not begin transaction");
291 ReleaseConnection();
292 return E_BEGIN_TRANSACTION_IN_READ_CONNECTION;
293 }
294
295 int errCode = connection->ExecuteSql("BEGIN EXCLUSIVE;");
296 if (errCode != E_OK) {
297 ReleaseConnection();
298 return errCode;
299 }
300 }
301
302 if (transactionObserver != nullptr) {
303 transactionObserver->OnBegin();
304 }
305
306 BaseTransaction transaction(connectionPool.getTransactionStack().size());
307 connectionPool.getTransactionStack().push(transaction);
308
309 return E_OK;
310 }
311
MarkAsCommitWithObserver(TransactionObserver * transactionObserver)312 int StoreSession::MarkAsCommitWithObserver(TransactionObserver *transactionObserver)
313 {
314 if (connectionPool.getTransactionStack().empty()) {
315 return E_NO_TRANSACTION_IN_SESSION;
316 }
317 connectionPool.getTransactionStack().top().SetMarkedSuccessful(true);
318 return E_OK;
319 }
320
EndTransactionWithObserver(TransactionObserver * transactionObserver)321 int StoreSession::EndTransactionWithObserver(TransactionObserver *transactionObserver)
322 {
323 if (connectionPool.getTransactionStack().empty()) {
324 return E_NO_TRANSACTION_IN_SESSION;
325 }
326
327 BaseTransaction transaction = connectionPool.getTransactionStack().top();
328 bool isSucceed = transaction.IsAllBeforeSuccessful() && transaction.IsMarkedSuccessful();
329 connectionPool.getTransactionStack().pop();
330
331 if (transactionObserver != nullptr) {
332 if (isSucceed) {
333 transactionObserver->OnCommit();
334 } else {
335 transactionObserver->OnRollback();
336 }
337 }
338
339 if (!connectionPool.getTransactionStack().empty()) {
340 if (transactionObserver != nullptr) {
341 transactionObserver->OnRollback();
342 }
343
344 if (!isSucceed) {
345 connectionPool.getTransactionStack().top().SetAllBeforeSuccessful(false);
346 }
347 } else {
348 int errCode;
349 if (isSucceed) {
350 errCode = connection->ExecuteSql("COMMIT;");
351 } else {
352 errCode = connection->ExecuteSql("ROLLBACK;");
353 }
354
355 ReleaseConnection();
356 return errCode;
357 }
358
359 return E_OK;
360 }
361
MarkAsCommit()362 int StoreSession::MarkAsCommit()
363 {
364 if (connectionPool.getTransactionStack().empty()) {
365 return E_NO_TRANSACTION_IN_SESSION;
366 }
367 connectionPool.getTransactionStack().top().SetMarkedSuccessful(true);
368 return E_OK;
369 }
370
EndTransaction()371 int StoreSession::EndTransaction()
372 {
373 if (connectionPool.getTransactionStack().empty()) {
374 return E_NO_TRANSACTION_IN_SESSION;
375 }
376
377 BaseTransaction transaction = connectionPool.getTransactionStack().top();
378 bool isSucceed = transaction.IsAllBeforeSuccessful() && transaction.IsMarkedSuccessful();
379 connectionPool.getTransactionStack().pop();
380 if (!connectionPool.getTransactionStack().empty()) {
381 if (!isSucceed) {
382 connectionPool.getTransactionStack().top().SetAllBeforeSuccessful(false);
383 }
384 } else {
385 int errCode = connection->ExecuteSql(isSucceed ? "COMMIT;" : "ROLLBACK;");
386 ReleaseConnection();
387 return errCode;
388 }
389
390 return E_OK;
391 }
IsInTransaction() const392 bool StoreSession::IsInTransaction() const
393 {
394 return !connectionPool.getTransactionStack().empty();
395 }
396
BeginStepQuery(int & errCode,const std::string & sql,const std::vector<std::string> & selectionArgs)397 std::shared_ptr<SqliteStatement> StoreSession::BeginStepQuery(
398 int &errCode, const std::string &sql, const std::vector<std::string> &selectionArgs)
399 {
400 if (isInStepQuery == true) {
401 LOG_ERROR("StoreSession BeginStepQuery fail : begin more step query in one session !");
402 errCode = E_MORE_STEP_QUERY_IN_ONE_SESSION;
403 return nullptr; // fail,already in
404 }
405
406 if (SqliteUtils::GetSqlStatementType(sql) != SqliteUtils::STATEMENT_SELECT) {
407 LOG_ERROR("StoreSession BeginStepQuery fail : not select sql !");
408 errCode = E_EXECUTE_IN_STEP_QUERY;
409 return nullptr;
410 }
411
412 AcquireConnection(true);
413 std::shared_ptr<SqliteStatement> statement = connection->BeginStepQuery(errCode, sql, selectionArgs);
414 if (statement == nullptr) {
415 ReleaseConnection();
416 return nullptr;
417 }
418 isInStepQuery = true;
419 return statement;
420 }
421
EndStepQuery()422 int StoreSession::EndStepQuery()
423 {
424 if (isInStepQuery == false) {
425 return E_OK;
426 }
427
428 int errCode = connection->EndStepQuery();
429 isInStepQuery = false;
430 ReleaseConnection();
431 return errCode;
432 }
433
ExecuteForSharedBlock(int & rowNum,std::string sql,const std::vector<ValueObject> & bindArgs,AppDataFwk::SharedBlock * sharedBlock,int startPos,int requiredPos,bool isCountAllRows)434 int StoreSession::ExecuteForSharedBlock(int &rowNum, std::string sql, const std::vector<ValueObject> &bindArgs,
435 AppDataFwk::SharedBlock *sharedBlock, int startPos, int requiredPos, bool isCountAllRows)
436 {
437 int errCode = BeginExecuteSql(sql);
438 if (errCode != E_OK) {
439 return errCode;
440 }
441 errCode =
442 connection->ExecuteForSharedBlock(rowNum, sql, bindArgs, sharedBlock, startPos, requiredPos, isCountAllRows);
443 ReleaseConnection();
444 return errCode;
445 }
446
BeginTransaction()447 int StoreSession::BeginTransaction()
448 {
449 AcquireConnection(false);
450
451 BaseTransaction transaction(connectionPool.getTransactionStack().size());
452 int errCode = connection->ExecuteSql(transaction.getTransactionStr());
453 if (errCode != E_OK) {
454 LOG_DEBUG("storeSession BeginTransaction Failed");
455 ReleaseConnection();
456 return errCode;
457 }
458 connectionPool.getTransactionStack().push(transaction);
459 ReleaseConnection();
460 return E_OK;
461 }
462
Commit()463 int StoreSession::Commit()
464 {
465 if (connectionPool.getTransactionStack().empty()) {
466 return E_OK;
467 }
468 BaseTransaction transaction = connectionPool.getTransactionStack().top();
469 std::string sqlStr = transaction.getCommitStr();
470 if (sqlStr.size() <= 1) {
471 connectionPool.getTransactionStack().pop();
472 return E_OK;
473 }
474
475 AcquireConnection(false);
476 int errCode = connection->ExecuteSql(sqlStr);
477 ReleaseConnection();
478 if (errCode != E_OK) {
479 // if error the transaction is leaving for rollback
480 return errCode;
481 }
482 connectionPool.getTransactionStack().pop();
483 return E_OK;
484 }
485
RollBack()486 int StoreSession::RollBack()
487 {
488 std::stack<BaseTransaction> transactionStack = connectionPool.getTransactionStack();
489 if (transactionStack.empty()) {
490 return E_NO_TRANSACTION_IN_SESSION;
491 }
492 BaseTransaction transaction = transactionStack.top();
493 transactionStack.pop();
494 if (transaction.getType() != TransType::ROLLBACK_SELF && !transactionStack.empty()) {
495 transactionStack.top().setChildFailure(true);
496 }
497 AcquireConnection(false);
498 int errCode = connection->ExecuteSql(transaction.getRollbackStr());
499 ReleaseConnection();
500 if (errCode != E_OK) {
501 LOG_ERROR("storeSession RollBack Fail");
502 }
503
504 return errCode;
505 }
506 } // namespace OHOS::NativeRdb
507