1//
2// SessionImpl.cpp
3//
4// Library: SQL/MySQL
5// Package: MySQL
6// Module: SessionImpl
7//
8// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/SQL/MySQL/SessionImpl.h"
16#include "Poco/SQL/MySQL/MySQLStatementImpl.h"
17#include "Poco/SQL/Session.h"
18#include "Poco/NumberParser.h"
19#include "Poco/String.h"
20
21
22namespace
23{
24 std::string copyStripped(std::string::const_iterator from, std::string::const_iterator to)
25 {
26 // skip leading spaces
27 while ((from != to) && isspace(*from)) from++;
28 // skip trailing spaces
29 while ((from != to) && isspace(*(to - 1))) to--;
30
31 return std::string(from, to);
32 }
33}
34
35
36namespace Poco {
37namespace SQL {
38namespace MySQL {
39
40
41const std::string SessionImpl::MYSQL_READ_UNCOMMITTED = "READ UNCOMMITTED";
42const std::string SessionImpl::MYSQL_READ_COMMITTED = "READ COMMITTED";
43const std::string SessionImpl::MYSQL_REPEATABLE_READ = "REPEATABLE READ";
44const std::string SessionImpl::MYSQL_SERIALIZABLE = "SERIALIZABLE";
45
46
47SessionImpl::SessionImpl(const std::string& connectionString, std::size_t loginTimeout) :
48 Poco::SQL::AbstractSessionImpl<SessionImpl>(connectionString, loginTimeout),
49 _connector("MySQL"),
50 _handle(0),
51 _connected(false),
52 _inTransaction(false)
53{
54 addProperty("insertId", &SessionImpl::setInsertId, &SessionImpl::getInsertId);
55 setProperty("handle", static_cast<MYSQL*>(_handle));
56 open();
57}
58
59
60void SessionImpl::open(const std::string& connect)
61{
62 if (connect != connectionString())
63 {
64 if (isConnected())
65 throw InvalidAccessException("Session already connected");
66
67 if (!connect.empty())
68 setConnectionString(connect);
69 }
70
71 poco_assert_dbg (!connectionString().empty());
72
73 _handle.init();
74
75 unsigned int timeout = static_cast<unsigned int>(getLoginTimeout());
76 _handle.options(MYSQL_OPT_CONNECT_TIMEOUT, timeout);
77
78 std::map<std::string, std::string> options;
79
80 // Default values
81 options["host"] = "localhost";
82 options["port"] = "3306";
83 options["user"] = "";
84 options["password"] = "";
85 options["db"] = "";
86 options["compress"] = "";
87 options["auto-reconnect"] = "";
88 options["secure-auth"] = "";
89 options["character-set"] = "utf8";
90
91 const std::string& connString = connectionString();
92 for (std::string::const_iterator start = connString.begin();;)
93 {
94 std::string::const_iterator finish = std::find(start, connString.end(), ';');
95 std::string::const_iterator middle = std::find(start, finish, '=');
96
97 if (middle == finish)
98 throw MySQLException("create session: bad connection string format, can not find '='");
99
100 options[copyStripped(start, middle)] = copyStripped(middle + 1, finish);
101
102 if ((finish == connString.end()) || (finish + 1 == connString.end())) break;
103
104 start = finish + 1;
105 }
106
107 if (options["user"].empty())
108 throw MySQLException("create session: specify user name");
109
110 const char * db = NULL;
111 if (!options["db"].empty())
112 db = options["db"].c_str();
113
114 unsigned int port = 0;
115 if (!NumberParser::tryParseUnsigned(options["port"], port) || 0 == port || port > 65535)
116 throw MySQLException("create session: specify correct port (numeric in decimal notation)");
117
118 if (options["compress"] == "true")
119 _handle.options(MYSQL_OPT_COMPRESS);
120 else if (options["compress"] == "false")
121 ;
122 else if (!options["compress"].empty())
123 throw MySQLException("create session: specify correct compress option (true or false) or skip it");
124
125 if (options["auto-reconnect"] == "true")
126 _handle.options(MYSQL_OPT_RECONNECT, true);
127 else if (options["auto-reconnect"] == "false")
128 _handle.options(MYSQL_OPT_RECONNECT, false);
129 else if (!options["auto-reconnect"].empty())
130 throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it");
131
132 if (options["secure-auth"] == "true")
133 _handle.options(MYSQL_SECURE_AUTH, true);
134 else if (options["secure-auth"] == "false")
135 _handle.options(MYSQL_SECURE_AUTH, false);
136 else if (!options["secure-auth"].empty())
137 throw MySQLException("create session: specify correct secure-auth option (true or false) or skip it");
138
139 if (!options["character-set"].empty())
140 _handle.options(MYSQL_SET_CHARSET_NAME, options["character-set"].c_str());
141
142 // Real connect
143 _handle.connect(options["host"].c_str(),
144 options["user"].c_str(),
145 options["password"].c_str(),
146 db,
147 port);
148
149 addFeature("autoCommit",
150 &SessionImpl::autoCommit,
151 &SessionImpl::isAutoCommit);
152
153 _connected = true;
154}
155
156
157SessionImpl::~SessionImpl()
158{
159 close();
160}
161
162
163StatementImpl::Ptr SessionImpl::createStatementImpl()
164{
165 return new MySQLStatementImpl(*this);
166}
167
168
169void SessionImpl::begin()
170{
171 Poco::FastMutex::ScopedLock l(_mutex);
172
173 if (_inTransaction)
174 throw Poco::InvalidAccessException("Already in transaction.");
175
176 _handle.startTransaction();
177 _inTransaction = true;
178}
179
180
181void SessionImpl::commit()
182{
183 _handle.commit();
184 _inTransaction = false;
185}
186
187
188void SessionImpl::rollback()
189{
190 _handle.rollback();
191 _inTransaction = false;
192}
193
194
195void SessionImpl::autoCommit(const std::string&, bool val)
196{
197 StatementExecutor ex(_handle);
198 ex.prepare(Poco::format("SET autocommit=%d", val ? 1 : 0));
199 ex.execute();
200}
201
202
203bool SessionImpl::isAutoCommit(const std::string&) const
204{
205 int ac = 0;
206 return 1 == getSetting("autocommit", ac);
207}
208
209
210void SessionImpl::setTransactionIsolation(Poco::UInt32 ti)
211{
212 std::string isolation;
213 switch (ti)
214 {
215 case Session::TRANSACTION_READ_UNCOMMITTED:
216 isolation = MYSQL_READ_UNCOMMITTED; break;
217 case Session::TRANSACTION_READ_COMMITTED:
218 isolation = MYSQL_READ_COMMITTED; break;
219 case Session::TRANSACTION_REPEATABLE_READ:
220 isolation = MYSQL_REPEATABLE_READ; break;
221 case Session::TRANSACTION_SERIALIZABLE:
222 isolation = MYSQL_SERIALIZABLE; break;
223 default:
224 throw Poco::InvalidArgumentException("setTransactionIsolation()");
225 }
226
227 StatementExecutor ex(_handle);
228 ex.prepare(Poco::format("SET SESSION TRANSACTION ISOLATION LEVEL %s", isolation));
229 ex.execute();
230}
231
232
233Poco::UInt32 SessionImpl::getTransactionIsolation() const
234{
235 std::string isolation;
236 getSetting("tx_isolation", isolation);
237 Poco::replaceInPlace(isolation, "-", " ");
238 if (MYSQL_READ_UNCOMMITTED == isolation)
239 return Session::TRANSACTION_READ_UNCOMMITTED;
240 else if (MYSQL_READ_COMMITTED == isolation)
241 return Session::TRANSACTION_READ_COMMITTED;
242 else if (MYSQL_REPEATABLE_READ == isolation)
243 return Session::TRANSACTION_REPEATABLE_READ;
244 else if (MYSQL_SERIALIZABLE == isolation)
245 return Session::TRANSACTION_SERIALIZABLE;
246
247 throw InvalidArgumentException("getTransactionIsolation()");
248}
249
250
251bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) const
252{
253 return Session::TRANSACTION_READ_UNCOMMITTED == ti ||
254 Session::TRANSACTION_READ_COMMITTED == ti ||
255 Session::TRANSACTION_REPEATABLE_READ == ti ||
256 Session::TRANSACTION_SERIALIZABLE == ti;
257}
258
259
260void SessionImpl::close()
261{
262 if (_connected)
263 {
264 _handle.close();
265 _connected = false;
266 }
267}
268
269
270void SessionImpl::reset()
271{
272 if (_connected)
273 _handle.reset();
274}
275
276
277void SessionImpl::setConnectionTimeout(std::size_t timeout)
278{
279 _handle.options(MYSQL_OPT_READ_TIMEOUT, static_cast<unsigned int>(timeout));
280 _handle.options(MYSQL_OPT_WRITE_TIMEOUT, static_cast<unsigned int>(timeout));
281 _timeout = timeout;
282}
283
284
285}}}
286