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 "sqlite_connection_pool.h"
17
18 #include <base_transaction.h>
19
20 #include <condition_variable>
21 #include <iostream>
22 #include <iterator>
23 #include <mutex>
24 #include <sstream>
25 #include <vector>
26
27 #include "logger.h"
28 #include "rdb_errno.h"
29 #include "sqlite_global_config.h"
30 #include "sqlite_utils.h"
31
32 namespace OHOS {
33 namespace NativeRdb {
34 using namespace OHOS::Rdb;
35
36 constexpr std::chrono::seconds TRANSACTION_TIMEOUT(2);
37
Create(const RdbStoreConfig & storeConfig,int & errCode)38 SqliteConnectionPool *SqliteConnectionPool::Create(const RdbStoreConfig &storeConfig, int &errCode)
39 {
40 auto pool = new (std::nothrow) SqliteConnectionPool(storeConfig);
41 if (pool == nullptr) {
42 LOG_ERROR("SqliteConnectionPool::Create new failed, pool is nullptr");
43 return nullptr;
44 }
45 errCode = pool->Init();
46 if (errCode != E_OK) {
47 delete pool;
48 return nullptr;
49 }
50 return pool;
51 }
52
SqliteConnectionPool(const RdbStoreConfig & storeConfig)53 SqliteConnectionPool::SqliteConnectionPool(const RdbStoreConfig &storeConfig)
54 : config_(storeConfig), writeConnection_(nullptr), writeConnectionUsed_(true), readConnections_(),
55 readConnectionCount_(0), idleReadConnectionCount_(0), transactionStack_(), transactionUsed_(false)
56 {
57 }
58
Init()59 int SqliteConnectionPool::Init()
60 {
61 int errCode = E_OK;
62 writeConnection_ = SqliteConnection::Open(config_, true, errCode);
63 if (writeConnection_ == nullptr) {
64 return errCode;
65 }
66
67 InitReadConnectionCount();
68
69 // max read connect count is 64
70 if (readConnectionCount_ > 64) {
71 return E_ARGS_READ_CON_OVERLOAD;
72 }
73 for (int i = 0; i < readConnectionCount_; i++) {
74 auto connection = SqliteConnection::Open(config_, false, errCode);
75 if (connection == nullptr) {
76 CloseAllConnections();
77 return errCode;
78 }
79 readConnections_.push_back(connection);
80 }
81
82 writeConnectionUsed_ = false;
83 idleReadConnectionCount_ = readConnectionCount_;
84 writeTimeout_ = std::chrono::seconds(config_.GetWriteTime());
85 readTimeout_ = std::chrono::seconds(config_.GetReadTime());
86 return E_OK;
87 }
88
~SqliteConnectionPool()89 SqliteConnectionPool::~SqliteConnectionPool()
90 {
91 CloseAllConnections();
92 }
93
InitReadConnectionCount()94 void SqliteConnectionPool::InitReadConnectionCount()
95 {
96 if (config_.GetStorageMode() == StorageMode::MODE_MEMORY) {
97 readConnectionCount_ = 0;
98 } else if (config_.GetJournalMode() == "WAL") {
99 readConnectionCount_ = config_.GetReadConSize();
100 } else {
101 readConnectionCount_ = 0;
102 }
103 }
104
CloseAllConnections()105 void SqliteConnectionPool::CloseAllConnections()
106 {
107 writeConnection_ = nullptr;
108 writeConnectionUsed_ = true;
109
110 for (auto &item : readConnections_) {
111 item = nullptr;
112 }
113 readConnections_.clear();
114 idleReadConnectionCount_ = 0;
115 }
116
AcquireConnection(bool isReadOnly)117 std::shared_ptr<SqliteConnection> SqliteConnectionPool::AcquireConnection(bool isReadOnly)
118 {
119 if (isReadOnly && readConnectionCount_ != 0) {
120 return AcquireReadConnection();
121 } else {
122 return AcquireWriteConnection();
123 }
124 }
125
ReleaseConnection(std::shared_ptr<SqliteConnection> connection)126 void SqliteConnectionPool::ReleaseConnection(std::shared_ptr<SqliteConnection> connection)
127 {
128 if (connection == nullptr) {
129 return;
130 }
131 connection->DesFinalize();
132 if (connection == writeConnection_) {
133 ReleaseWriteConnection();
134 connection->TryCheckPoint();
135 } else {
136 ReleaseReadConnection(connection);
137 }
138 }
139
AcquireWriteConnection()140 std::shared_ptr<SqliteConnection> SqliteConnectionPool::AcquireWriteConnection()
141 {
142 std::unique_lock<std::mutex> lock(writeMutex_);
143 if (writeCondition_.wait_for(lock, writeTimeout_, [this] {
144 return !writeConnectionUsed_;
145 })) {
146 writeConnectionUsed_ = true;
147 return writeConnection_;
148 }
149 LOG_WARN("writeConnection_ is %{public}d", writeConnectionUsed_);
150 return nullptr;
151 }
152
AcquireTransaction()153 int SqliteConnectionPool::AcquireTransaction()
154 {
155 std::unique_lock<std::mutex> lock(transMutex_);
156 if (transCondition_.wait_for(lock, TRANSACTION_TIMEOUT, [this] {
157 return !transactionUsed_;
158 })) {
159 transactionUsed_ = true;
160 return E_OK;
161 }
162 LOG_WARN("transactionUsed_ is %{public}d", transactionUsed_);
163 return E_TRANSACTION_IN_EXECUTE;
164 }
165
ReleaseTransaction()166 void SqliteConnectionPool::ReleaseTransaction()
167 {
168 {
169 std::unique_lock<std::mutex> lock(transMutex_);
170 transactionUsed_ = false;
171 }
172 transCondition_.notify_one();
173 }
174
ReleaseWriteConnection()175 void SqliteConnectionPool::ReleaseWriteConnection()
176 {
177 {
178 std::unique_lock<std::mutex> lock(writeMutex_);
179 writeConnectionUsed_ = false;
180 }
181 writeCondition_.notify_one();
182 }
183
184 /**
185 * get last element from connectionPool
186 * @return
187 */
AcquireReadConnection()188 std::shared_ptr<SqliteConnection> SqliteConnectionPool::AcquireReadConnection()
189 {
190 std::unique_lock<std::mutex> lock(readMutex_);
191 if (readCondition_.wait_for(lock, readTimeout_, [this] {
192 return idleReadConnectionCount_ > 0;
193 })) {
194 auto connection = readConnections_.back();
195 readConnections_.pop_back();
196 idleReadConnectionCount_--;
197 return connection;
198 }
199 LOG_WARN("readConnectionCount_ is %{public}d, idleReadConnectionCount_ is %{public}d", readConnectionCount_,
200 idleReadConnectionCount_);
201 return nullptr;
202 }
203
204 /**
205 * push connection back to last of connectionPool
206 * @param connection
207 */
ReleaseReadConnection(std::shared_ptr<SqliteConnection> connection)208 void SqliteConnectionPool::ReleaseReadConnection(std::shared_ptr<SqliteConnection> connection)
209 {
210 {
211 std::unique_lock<std::mutex> lock(readMutex_);
212 readConnections_.push_back(connection);
213 idleReadConnectionCount_++;
214 }
215 readCondition_.notify_one();
216 }
217
InnerReOpenReadConnections()218 int SqliteConnectionPool::InnerReOpenReadConnections()
219 {
220 int errCode = E_OK;
221 for (auto &item : readConnections_) {
222 item = nullptr;
223 }
224 readConnections_.clear();
225
226 for (int i = 0; i < readConnectionCount_; i++) {
227 auto connection = SqliteConnection::Open(config_, false, errCode);
228 if (connection == nullptr) {
229 CloseAllConnections();
230 return errCode;
231 }
232 readConnections_.push_back(connection);
233 }
234
235 return errCode;
236 }
237
ReOpenAvailableReadConnections()238 int SqliteConnectionPool::ReOpenAvailableReadConnections()
239 {
240 std::unique_lock<std::mutex> lock(readMutex_);
241 return InnerReOpenReadConnections();
242 }
243
244 #ifdef RDB_SUPPORT_ICU
245 /**
246 * The database locale.
247 */
ConfigLocale(const std::string localeStr)248 int SqliteConnectionPool::ConfigLocale(const std::string localeStr)
249 {
250 std::unique_lock<std::mutex> lock(rdbMutex_);
251 if (idleReadConnectionCount_ != readConnectionCount_) {
252 return E_NO_ROW_IN_QUERY;
253 }
254
255 for (int i = 0; i < idleReadConnectionCount_; i++) {
256 auto connection = readConnections_[i];
257 if (connection == nullptr) {
258 LOG_ERROR("Read Connection is null.");
259 return E_ERROR;
260 }
261 connection->ConfigLocale(localeStr);
262 }
263
264 if (writeConnection_ == nullptr) {
265 LOG_ERROR("Write Connection is null.");
266 return E_ERROR;
267 } else {
268 writeConnection_->ConfigLocale(localeStr);
269 }
270
271 return E_OK;
272 }
273 #endif
274
275 /**
276 * Rename the backed up database.
277 */
ChangeDbFileForRestore(const std::string newPath,const std::string backupPath,const std::vector<uint8_t> & newKey)278 int SqliteConnectionPool::ChangeDbFileForRestore(const std::string newPath, const std::string backupPath,
279 const std::vector<uint8_t> &newKey)
280 {
281 if (writeConnectionUsed_ == true || idleReadConnectionCount_ != readConnectionCount_) {
282 LOG_ERROR("Connection pool is busy now!");
283 return E_ERROR;
284 }
285
286 LOG_ERROR("restore.");
287 CloseAllConnections();
288
289 std::string currentPath = config_.GetPath();
290 bool ret = SqliteUtils::DeleteFile(currentPath);
291 if (ret == false) {
292 LOG_ERROR("DeleteFile error");
293 }
294 SqliteUtils::DeleteFile(currentPath + "-shm");
295 SqliteUtils::DeleteFile(currentPath + "-wal");
296 SqliteUtils::DeleteFile(currentPath + "-journal");
297
298 if (currentPath != newPath) {
299 SqliteUtils::DeleteFile(newPath);
300 SqliteUtils::DeleteFile(newPath + "-shm");
301 SqliteUtils::DeleteFile(newPath + "-wal");
302 SqliteUtils::DeleteFile(newPath + "-journal");
303 }
304
305 int retVal = SqliteUtils::RenameFile(backupPath, newPath);
306 if (retVal != E_OK) {
307 LOG_ERROR("RenameFile error");
308 return retVal;
309 }
310
311 return Init();
312 }
313
GetTransactionStack()314 std::stack<BaseTransaction> &SqliteConnectionPool::GetTransactionStack()
315 {
316 return transactionStack_;
317 }
318
GetTransactionStackMutex()319 std::mutex &SqliteConnectionPool::GetTransactionStackMutex()
320 {
321 return transactionStackMutex_;
322 }
323 } // namespace NativeRdb
324 } // namespace OHOS
325