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
38namespace Poco {
39namespace SQL {
40namespace SQLite {
41
42
43const std::string SessionImpl::DEFERRED_BEGIN_TRANSACTION("BEGIN DEFERRED");
44const std::string SessionImpl::COMMIT_TRANSACTION("COMMIT");
45const std::string SessionImpl::ABORT_TRANSACTION("ROLLBACK");
46
47
48SessionImpl::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
65SessionImpl::~SessionImpl()
66{
67 try
68 {
69 close();
70 }
71 catch (...)
72 {
73 poco_unexpected();
74 }
75}
76
77
78StatementImpl::Ptr SessionImpl::createStatementImpl()
79{
80 poco_check_ptr (_pDB);
81 return new SQLiteStatementImpl(*this, _pDB);
82}
83
84
85void 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
95void 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
105void 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
115void SessionImpl::setTransactionIsolation(Poco::UInt32 ti)
116{
117 if (ti != Session::TRANSACTION_READ_COMMITTED)
118 throw Poco::InvalidArgumentException("setTransactionIsolation()");
119}
120
121
122Poco::UInt32 SessionImpl::getTransactionIsolation() const
123{
124 return Session::TRANSACTION_READ_COMMITTED;
125}
126
127
128bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) const
129{
130 if (ti == Session::TRANSACTION_READ_COMMITTED) return true;
131 return false;
132}
133
134
135bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) const
136{
137 if (ti == Session::TRANSACTION_READ_COMMITTED) return true;
138 return false;
139}
140
141
142void 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
187void 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
219void SessionImpl::reset()
220{
221}
222
223
224bool SessionImpl::isConnected() const
225{
226 return _connected;
227}
228
229
230void 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
247void SessionImpl::setConnectionTimeout(const std::string& /*prop*/, const Poco::Any& value)
248{
249 setConnectionTimeout(Poco::RefAnyCast<std::size_t>(value));
250}
251
252
253Poco::Any SessionImpl::getConnectionTimeout(const std::string& /*prop*/) const
254{
255 return Poco::Any(_timeout/1000);
256}
257
258
259void 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
269bool 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.
280sqlite3* Utility::dbHandle(const Session& session)
281{
282 return AnyCast<sqlite3*>(session.getProperty("handle"));
283}
284
285
286} } } // namespace Poco::SQL::SQLite
287