1 | // |
2 | // SessionImpl.cpp |
3 | // |
4 | // Library: SQL/SQLite |
5 | // Package: SQLite |
6 | // Module: SessionImpl |
7 | // |
8 | // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/SQL/SQLite/SessionImpl.h" |
16 | #include "Poco/SQL/SQLite/Utility.h" |
17 | #include "Poco/SQL/SQLite/SQLiteStatementImpl.h" |
18 | #include "Poco/SQL/SQLite/SQLiteException.h" |
19 | #include "Poco/SQL/Session.h" |
20 | #include "Poco/Stopwatch.h" |
21 | #include "Poco/String.h" |
22 | #include "Poco/Mutex.h" |
23 | #include "Poco/SQL/SQLException.h" |
24 | #if defined(POCO_UNBUNDLED) |
25 | #include <sqlite3.h> |
26 | #else |
27 | #include "sqlite3.h" |
28 | #endif |
29 | #include <cstdlib> |
30 | #include <limits> |
31 | |
32 | |
33 | #ifndef SQLITE_OPEN_URI |
34 | #define SQLITE_OPEN_URI 0 |
35 | #endif |
36 | |
37 | |
38 | namespace Poco { |
39 | namespace SQL { |
40 | namespace SQLite { |
41 | |
42 | |
43 | const std::string SessionImpl::DEFERRED_BEGIN_TRANSACTION("BEGIN DEFERRED" ); |
44 | const std::string SessionImpl::COMMIT_TRANSACTION("COMMIT" ); |
45 | const std::string SessionImpl::ABORT_TRANSACTION("ROLLBACK" ); |
46 | |
47 | |
48 | SessionImpl::SessionImpl(const std::string& fileName, std::size_t loginTimeout): |
49 | Poco::SQL::AbstractSessionImpl<SessionImpl>(fileName, loginTimeout), |
50 | _connector(Connector::KEY), |
51 | _pDB(0), |
52 | _connected(false), |
53 | _isTransaction(false) |
54 | { |
55 | open(); |
56 | setConnectionTimeout(loginTimeout); |
57 | setProperty("handle" , _pDB); |
58 | addFeature("autoCommit" , |
59 | &SessionImpl::autoCommit, |
60 | &SessionImpl::isAutoCommit); |
61 | addProperty("connectionTimeout" , &SessionImpl::setConnectionTimeout, &SessionImpl::getConnectionTimeout); |
62 | } |
63 | |
64 | |
65 | SessionImpl::~SessionImpl() |
66 | { |
67 | try |
68 | { |
69 | close(); |
70 | } |
71 | catch (...) |
72 | { |
73 | poco_unexpected(); |
74 | } |
75 | } |
76 | |
77 | |
78 | StatementImpl::Ptr SessionImpl::createStatementImpl() |
79 | { |
80 | poco_check_ptr (_pDB); |
81 | return new SQLiteStatementImpl(*this, _pDB); |
82 | } |
83 | |
84 | |
85 | void SessionImpl::begin() |
86 | { |
87 | Poco::Mutex::ScopedLock l(_mutex); |
88 | SQLiteStatementImpl tmp(*this, _pDB); |
89 | tmp.add(DEFERRED_BEGIN_TRANSACTION); |
90 | tmp.execute(); |
91 | _isTransaction = true; |
92 | } |
93 | |
94 | |
95 | void SessionImpl::commit() |
96 | { |
97 | Poco::Mutex::ScopedLock l(_mutex); |
98 | SQLiteStatementImpl tmp(*this, _pDB); |
99 | tmp.add(COMMIT_TRANSACTION); |
100 | tmp.execute(); |
101 | _isTransaction = false; |
102 | } |
103 | |
104 | |
105 | void SessionImpl::rollback() |
106 | { |
107 | Poco::Mutex::ScopedLock l(_mutex); |
108 | SQLiteStatementImpl tmp(*this, _pDB); |
109 | tmp.add(ABORT_TRANSACTION); |
110 | tmp.execute(); |
111 | _isTransaction = false; |
112 | } |
113 | |
114 | |
115 | void SessionImpl::setTransactionIsolation(Poco::UInt32 ti) |
116 | { |
117 | if (ti != Session::TRANSACTION_READ_COMMITTED) |
118 | throw Poco::InvalidArgumentException("setTransactionIsolation()" ); |
119 | } |
120 | |
121 | |
122 | Poco::UInt32 SessionImpl::getTransactionIsolation() const |
123 | { |
124 | return Session::TRANSACTION_READ_COMMITTED; |
125 | } |
126 | |
127 | |
128 | bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) const |
129 | { |
130 | if (ti == Session::TRANSACTION_READ_COMMITTED) return true; |
131 | return false; |
132 | } |
133 | |
134 | |
135 | bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) const |
136 | { |
137 | if (ti == Session::TRANSACTION_READ_COMMITTED) return true; |
138 | return false; |
139 | } |
140 | |
141 | |
142 | void SessionImpl::open(const std::string& connect) |
143 | { |
144 | if (connect != connectionString()) |
145 | { |
146 | if (isConnected()) |
147 | throw InvalidAccessException("Session already connected" ); |
148 | |
149 | if (!connect.empty()) |
150 | setConnectionString(connect); |
151 | } |
152 | |
153 | poco_assert_dbg (!connectionString().empty()); |
154 | |
155 | try |
156 | { |
157 | int rc = 0; |
158 | size_t tout = getLoginTimeout(); |
159 | Stopwatch sw; sw.start(); |
160 | while (true) |
161 | { |
162 | rc = sqlite3_open_v2(connectionString().c_str(), &_pDB, |
163 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, NULL); |
164 | if (rc == SQLITE_OK) break; |
165 | if (sw.elapsedSeconds() >= tout) |
166 | { |
167 | close(); |
168 | poco_assert(_pDB); |
169 | Utility::throwException(_pDB, rc); |
170 | } |
171 | else Thread::sleep(10); |
172 | } |
173 | } |
174 | catch (SQLiteException& ex) |
175 | { |
176 | throw ConnectionFailedException(ex.displayText()); |
177 | } |
178 | catch (AssertionViolationException& ex) |
179 | { |
180 | throw ConnectionFailedException(ex.displayText()); |
181 | } |
182 | |
183 | _connected = true; |
184 | } |
185 | |
186 | |
187 | void SessionImpl::close() |
188 | { |
189 | if (_pDB) |
190 | { |
191 | int result = 0; |
192 | int times = 10; |
193 | do |
194 | { |
195 | result = sqlite3_close_v2(_pDB); |
196 | } while (SQLITE_BUSY == result && --times > 0); |
197 | |
198 | if (SQLITE_BUSY == result && times == 0) |
199 | { |
200 | times = 10; |
201 | sqlite3_stmt *pStmt = NULL; |
202 | do |
203 | { |
204 | pStmt = sqlite3_next_stmt(_pDB, NULL); |
205 | if (pStmt && sqlite3_stmt_busy(pStmt)) |
206 | { |
207 | sqlite3_finalize(pStmt); |
208 | } |
209 | } while (pStmt != NULL && --times > 0); |
210 | sqlite3_close_v2(_pDB); |
211 | } |
212 | _pDB = 0; |
213 | } |
214 | |
215 | _connected = false; |
216 | } |
217 | |
218 | |
219 | void SessionImpl::reset() |
220 | { |
221 | } |
222 | |
223 | |
224 | bool SessionImpl::isConnected() const |
225 | { |
226 | return _connected; |
227 | } |
228 | |
229 | |
230 | void SessionImpl::setConnectionTimeout(std::size_t timeout) |
231 | { |
232 | if(timeout <= std::numeric_limits<int>::max()/1000) |
233 | { |
234 | int tout = 1000 * static_cast<int>(timeout); |
235 | int rc = sqlite3_busy_timeout(_pDB, tout); |
236 | if (rc != 0) Utility::throwException(_pDB, rc); |
237 | _timeout = tout; |
238 | } |
239 | else |
240 | { |
241 | throw RangeException |
242 | ("Occurred integer overflow because of timeout value." ); |
243 | } |
244 | } |
245 | |
246 | |
247 | void SessionImpl::setConnectionTimeout(const std::string& /*prop*/, const Poco::Any& value) |
248 | { |
249 | setConnectionTimeout(Poco::RefAnyCast<std::size_t>(value)); |
250 | } |
251 | |
252 | |
253 | Poco::Any SessionImpl::getConnectionTimeout(const std::string& /*prop*/) const |
254 | { |
255 | return Poco::Any(_timeout/1000); |
256 | } |
257 | |
258 | |
259 | void SessionImpl::autoCommit(const std::string&, bool) |
260 | { |
261 | // The problem here is to decide whether to call commit or rollback |
262 | // when autocommit is set to true. Hence, it is best not to implement |
263 | // this explicit call and only implicitly support autocommit setting. |
264 | throw NotImplementedException( |
265 | "SQLite autocommit is implicit with begin/commit/rollback." ); |
266 | } |
267 | |
268 | |
269 | bool SessionImpl::isAutoCommit(const std::string&) const |
270 | { |
271 | Poco::Mutex::ScopedLock l(_mutex); |
272 | return (0 != sqlite3_get_autocommit(_pDB)); |
273 | } |
274 | |
275 | |
276 | // NOTE: Utility::dbHandle() has been moved here from Utility.cpp |
277 | // as a workaround for a failing AnyCast with Clang. |
278 | // See <https://github.com/pocoproject/poco/issues/578> |
279 | // for a discussion. |
280 | sqlite3* Utility::dbHandle(const Session& session) |
281 | { |
282 | return AnyCast<sqlite3*>(session.getProperty("handle" )); |
283 | } |
284 | |
285 | |
286 | } } } // namespace Poco::SQL::SQLite |
287 | |