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 WAIT_CONNECT_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 SqliteConnection *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 return E_OK;
85 }
86
~SqliteConnectionPool()87 SqliteConnectionPool::~SqliteConnectionPool()
88 {
89 CloseAllConnections();
90 }
91
InitReadConnectionCount()92 void SqliteConnectionPool::InitReadConnectionCount()
93 {
94 if (config.GetStorageMode() == StorageMode::MODE_MEMORY) {
95 readConnectionCount = 0;
96 } else if (config.GetJournalMode() == "WAL") {
97 readConnectionCount = config.GetReadConSize();
98 } else {
99 readConnectionCount = 0;
100 }
101 }
102
CloseAllConnections()103 void SqliteConnectionPool::CloseAllConnections()
104 {
105 if (writeConnection != nullptr) {
106 delete writeConnection;
107 }
108 writeConnection = nullptr;
109 writeConnectionUsed = true;
110
111 for (auto &item : readConnections) {
112 if (item != nullptr) {
113 delete item;
114 item = nullptr;
115 }
116 }
117 readConnections.clear();
118 idleReadConnectionCount = 0;
119 }
120
AcquireConnection(bool isReadOnly)121 SqliteConnection *SqliteConnectionPool::AcquireConnection(bool isReadOnly)
122 {
123 if (isReadOnly && readConnectionCount != 0) {
124 return AcquireReadConnection();
125 } else {
126 return AcquireWriteConnection();
127 }
128 }
ReleaseConnection(SqliteConnection * connection)129 void SqliteConnectionPool::ReleaseConnection(SqliteConnection *connection)
130 {
131 if (connection == nullptr) {
132 return;
133 }
134 connection->DesFinalize();
135 if (connection == writeConnection) {
136 ReleaseWriteConnection();
137 } else {
138 ReleaseReadConnection(connection);
139 }
140 }
141
AcquireWriteConnection()142 SqliteConnection *SqliteConnectionPool::AcquireWriteConnection()
143 {
144 std::unique_lock<std::mutex> lock(writeMutex);
145 if (writeCondition.wait_for(lock, WAIT_CONNECT_TIMEOUT, [this] { return !writeConnectionUsed; })) {
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, WAIT_CONNECT_TIMEOUT, [this] { return !transactionUsed; })) {
157 transactionUsed = true;
158 return E_OK;
159 }
160 LOG_WARN("transactionUsed is %{public}d", transactionUsed);
161 return E_TRANSACTION_IN_EXECUTE;
162 }
163
ReleaseTransaction()164 void SqliteConnectionPool::ReleaseTransaction()
165 {
166 {
167 std::unique_lock<std::mutex> lock(transMutex);
168 transactionUsed = false;
169 }
170 transCondition.notify_one();
171 }
172
ReleaseWriteConnection()173 void SqliteConnectionPool::ReleaseWriteConnection()
174 {
175 {
176 std::unique_lock<std::mutex> lock(writeMutex);
177 writeConnectionUsed = false;
178 }
179 writeCondition.notify_one();
180 }
181
182 /**
183 * get last element from connectionPool
184 * @return
185 */
AcquireReadConnection()186 SqliteConnection *SqliteConnectionPool::AcquireReadConnection()
187 {
188 std::unique_lock<std::mutex> lock(readMutex);
189 if (readCondition.wait_for(lock, WAIT_CONNECT_TIMEOUT, [this] { return idleReadConnectionCount > 0; })) {
190 SqliteConnection *connection = readConnections.back();
191 readConnections.pop_back();
192 idleReadConnectionCount--;
193 return connection;
194 }
195 LOG_WARN("readConnectionCount is %{public}d, idleReadConnectionCount is %{public}d", readConnectionCount,
196 idleReadConnectionCount);
197 return nullptr;
198 }
199
200 /**
201 * push connection back to last of connectionPool
202 * @param connection
203 */
ReleaseReadConnection(SqliteConnection * connection)204 void SqliteConnectionPool::ReleaseReadConnection(SqliteConnection *connection)
205 {
206 {
207 std::unique_lock<std::mutex> lock(readMutex);
208 readConnections.push_back(connection);
209 idleReadConnectionCount++;
210 }
211 readCondition.notify_one();
212 }
213
IsOverLength(const std::vector<uint8_t> & newKey)214 bool SqliteConnectionPool::IsOverLength(const std::vector<uint8_t> &newKey)
215 {
216 if (newKey.empty()) {
217 return false;
218 }
219
220 std::stringstream ss;
221 copy(newKey.begin(), newKey.end(), std::ostream_iterator<uint8_t>(ss, ""));
222 return ss.str().length() > LIMITATION;
223 }
224
InnerReOpenReadConnections()225 int SqliteConnectionPool::InnerReOpenReadConnections()
226 {
227 int errCode = E_OK;
228 for (auto &item : readConnections) {
229 if (item != nullptr) {
230 delete item;
231 item = nullptr;
232 }
233 }
234 readConnections.clear();
235
236 for (int i = 0; i < readConnectionCount; i++) {
237 SqliteConnection *connection = SqliteConnection::Open(config, false, errCode);
238 if (connection == nullptr) {
239 CloseAllConnections();
240 return errCode;
241 }
242 readConnections.push_back(connection);
243 }
244
245 return errCode;
246 }
247
248
ReOpenAvailableReadConnections()249 int SqliteConnectionPool::ReOpenAvailableReadConnections()
250 {
251 std::unique_lock<std::mutex> lock(readMutex);
252 return InnerReOpenReadConnections();
253 }
254
255 #ifdef RDB_SUPPORT_ICU
256 /**
257 * The database locale.
258 */
ConfigLocale(const std::string localeStr)259 int SqliteConnectionPool::ConfigLocale(const std::string localeStr)
260 {
261 std::unique_lock<std::mutex> lock(rdbMutex);
262 if (idleReadConnectionCount != readConnectionCount) {
263 return E_NO_ROW_IN_QUERY;
264 }
265
266 for (int i = 0; i < idleReadConnectionCount; i++) {
267 SqliteConnection *connection = readConnections[i];
268 if (connection == nullptr) {
269 LOG_ERROR("Read Connection is null.");
270 return E_ERROR;
271 }
272 connection->ConfigLocale(localeStr);
273 }
274
275 if (writeConnection == nullptr) {
276 LOG_ERROR("Write Connection is null.");
277 return E_ERROR;
278 } else {
279 writeConnection->ConfigLocale(localeStr);
280 }
281
282 return E_OK;
283 }
284 #endif
285
286 /**
287 * Rename the backed up database.
288 */
ChangeDbFileForRestore(const std::string newPath,const std::string backupPath,const std::vector<uint8_t> & newKey)289 int SqliteConnectionPool::ChangeDbFileForRestore(const std::string newPath, const std::string backupPath,
290 const std::vector<uint8_t> &newKey)
291 {
292 if (writeConnectionUsed == true || idleReadConnectionCount != readConnectionCount) {
293 LOG_ERROR("Connection pool is busy now!");
294 return E_ERROR;
295 }
296
297 LOG_ERROR("restore.");
298 CloseAllConnections();
299
300 std::string currentPath = config.GetPath();
301 bool ret = SqliteUtils::DeleteFile(currentPath);
302 if (ret == false) {
303 LOG_ERROR("DeleteFile error");
304 }
305 SqliteUtils::DeleteFile(currentPath + "-shm");
306 SqliteUtils::DeleteFile(currentPath + "-wal");
307 SqliteUtils::DeleteFile(currentPath + "-journal");
308
309 if (currentPath != newPath) {
310 SqliteUtils::DeleteFile(newPath);
311 SqliteUtils::DeleteFile(newPath + "-shm");
312 SqliteUtils::DeleteFile(newPath + "-wal");
313 SqliteUtils::DeleteFile(newPath + "-journal");
314 }
315
316 int retVal = SqliteUtils::RenameFile(backupPath, newPath);
317 if (retVal != E_OK) {
318 LOG_ERROR("RenameFile error");
319 return retVal;
320 }
321
322 return Init();
323 }
324
getTransactionStack()325 std::stack<BaseTransaction> &SqliteConnectionPool::getTransactionStack()
326 {
327 return transactionStack;
328 }
329 } // namespace NativeRdb
330 } // namespace OHOS
331