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 | |
22 | namespace |
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 | |
36 | namespace Poco { |
37 | namespace SQL { |
38 | namespace MySQL { |
39 | |
40 | |
41 | const std::string SessionImpl::MYSQL_READ_UNCOMMITTED = "READ UNCOMMITTED" ; |
42 | const std::string SessionImpl::MYSQL_READ_COMMITTED = "READ COMMITTED" ; |
43 | const std::string SessionImpl::MYSQL_REPEATABLE_READ = "REPEATABLE READ" ; |
44 | const std::string SessionImpl::MYSQL_SERIALIZABLE = "SERIALIZABLE" ; |
45 | |
46 | |
47 | SessionImpl::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 | |
60 | void 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 | |
157 | SessionImpl::~SessionImpl() |
158 | { |
159 | close(); |
160 | } |
161 | |
162 | |
163 | StatementImpl::Ptr SessionImpl::createStatementImpl() |
164 | { |
165 | return new MySQLStatementImpl(*this); |
166 | } |
167 | |
168 | |
169 | void 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 | |
181 | void SessionImpl::commit() |
182 | { |
183 | _handle.commit(); |
184 | _inTransaction = false; |
185 | } |
186 | |
187 | |
188 | void SessionImpl::rollback() |
189 | { |
190 | _handle.rollback(); |
191 | _inTransaction = false; |
192 | } |
193 | |
194 | |
195 | void 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 | |
203 | bool SessionImpl::isAutoCommit(const std::string&) const |
204 | { |
205 | int ac = 0; |
206 | return 1 == getSetting("autocommit" , ac); |
207 | } |
208 | |
209 | |
210 | void 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 | |
233 | Poco::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 | |
251 | bool 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 | |
260 | void SessionImpl::close() |
261 | { |
262 | if (_connected) |
263 | { |
264 | _handle.close(); |
265 | _connected = false; |
266 | } |
267 | } |
268 | |
269 | |
270 | void SessionImpl::reset() |
271 | { |
272 | if (_connected) |
273 | _handle.reset(); |
274 | } |
275 | |
276 | |
277 | void 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 | |