1//
2// SessionImpl.cpp
3//
4// Library: Data/ODBC
5// Package: ODBC
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/Data/ODBC/SessionImpl.h"
16#include "Poco/Data/ODBC/Utility.h"
17#include "Poco/Data/ODBC/ODBCStatementImpl.h"
18#include "Poco/Data/ODBC/Error.h"
19#include "Poco/Data/ODBC/ODBCException.h"
20#include "Poco/Data/Session.h"
21#include "Poco/String.h"
22#include <sqlext.h>
23
24
25namespace Poco {
26namespace Data {
27namespace ODBC {
28
29
30SessionImpl::SessionImpl(const std::string& connect,
31 std::size_t loginTimeout,
32 std::size_t maxFieldSize,
33 bool autoBind,
34 bool autoExtract):
35 Poco::Data::AbstractSessionImpl<SessionImpl>(connect, loginTimeout),
36 _connector(Connector::KEY),
37 _maxFieldSize(maxFieldSize),
38 _autoBind(autoBind),
39 _autoExtract(autoExtract),
40 _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN),
41 _inTransaction(false),
42 _queryTimeout(-1)
43{
44 init();
45}
46
47
48SessionImpl::SessionImpl(const std::string& connect,
49 Poco::Any maxFieldSize,
50 bool enforceCapability,
51 bool autoBind,
52 bool autoExtract): Poco::Data::AbstractSessionImpl<SessionImpl>(connect),
53 _connector(Connector::KEY),
54 _maxFieldSize(maxFieldSize),
55 _autoBind(autoBind),
56 _autoExtract(autoExtract),
57 _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN),
58 _inTransaction(false),
59 _queryTimeout(-1)
60{
61 init();
62}
63
64
65void SessionImpl::init()
66{
67 setFeature("bulk", true);
68 open();
69 setProperty("handle", _db.handle());
70 setProperty("handle", _db.handle());
71}
72
73
74SessionImpl::~SessionImpl()
75{
76 try
77 {
78 if (static_cast<bool>(_db) && isTransaction() && !getFeature("autoCommit"))
79 {
80 try { rollback(); }
81 catch (...) { }
82 }
83
84 close();
85 }
86 catch (...)
87 {
88 poco_unexpected();
89 }
90}
91
92
93Poco::Data::StatementImpl* SessionImpl::createStatementImpl()
94{
95 return new ODBCStatementImpl(*this);
96}
97
98
99void SessionImpl::open(const std::string& connect)
100{
101 if (connect != connectionString())
102 {
103 if (isConnected())
104 throw InvalidAccessException("Session already connected");
105
106 if (!connect.empty())
107 setConnectionString(connect);
108 }
109
110 poco_assert_dbg (!connectionString().empty());
111
112 SQLULEN tout = static_cast<SQLULEN>(getLoginTimeout());
113 if (Utility::isError(Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)tout, 0)))
114 {
115 if (Utility::isError(Poco::Data::ODBC::SQLGetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, &tout, 0, 0)) ||
116 getLoginTimeout() != tout)
117 {
118 ConnectionException e(_db);
119 throw ConnectionFailedException(e.errorString(), e);
120 }
121 }
122
123 SQLCHAR connectOutput[512] = {0};
124 SQLSMALLINT result;
125
126 if (Utility::isError(Poco::Data::ODBC::SQLDriverConnect(_db
127 , NULL
128 ,(SQLCHAR*) connectionString().c_str()
129 ,(SQLSMALLINT) SQL_NTS
130 , connectOutput
131 , sizeof(connectOutput)
132 , &result
133 , SQL_DRIVER_NOPROMPT)))
134 {
135 ConnectionException e(_db);
136 close();
137 throw ConnectionFailedException(e.errorString(), e);
138 }
139
140 _dataTypes.fillTypeInfo(_db);
141
142 addProperty("dataTypeInfo",
143 &SessionImpl::setDataTypeInfo,
144 &SessionImpl::dataTypeInfo);
145
146 addFeature("autoCommit",
147 &SessionImpl::autoCommit,
148 &SessionImpl::isAutoCommit);
149
150 addFeature("autoBind",
151 &SessionImpl::autoBind,
152 &SessionImpl::isAutoBind);
153
154 addFeature("autoExtract",
155 &SessionImpl::autoExtract,
156 &SessionImpl::isAutoExtract);
157
158 addProperty("maxFieldSize",
159 &SessionImpl::setMaxFieldSize,
160 &SessionImpl::getMaxFieldSize);
161
162 addProperty("queryTimeout",
163 &SessionImpl::setQueryTimeout,
164 &SessionImpl::getQueryTimeout);
165
166 Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
167
168 if (!canTransact()) autoCommit("", true);
169}
170
171
172bool SessionImpl::isConnected()
173{
174 SQLULEN value = 0;
175
176 if (!static_cast<bool>(_db) || Utility::isError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
177 SQL_ATTR_CONNECTION_DEAD,
178 &value,
179 0,
180 0))) return false;
181
182 return (SQL_CD_FALSE == value);
183}
184
185
186void SessionImpl::setConnectionTimeout(std::size_t timeout)
187{
188 SQLUINTEGER value = static_cast<SQLUINTEGER>(timeout);
189
190 checkError(Poco::Data::ODBC::SQLSetConnectAttr(_db,
191 SQL_ATTR_CONNECTION_TIMEOUT,
192 &value,
193 SQL_IS_UINTEGER), "Failed to set connection timeout.");
194}
195
196
197std::size_t SessionImpl::getConnectionTimeout()
198{
199 SQLULEN value = 0;
200
201 checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
202 SQL_ATTR_CONNECTION_TIMEOUT,
203 &value,
204 0,
205 0), "Failed to get connection timeout.");
206
207 return value;
208}
209
210
211bool SessionImpl::canTransact()
212{
213 if (ODBC_TXN_CAPABILITY_UNKNOWN == _canTransact)
214 {
215 SQLUSMALLINT ret;
216 SQLRETURN res = Poco::Data::ODBC::SQLGetInfo(_db, SQL_TXN_CAPABLE, &ret, 0, 0);
217 if (!Utility::isError(res))
218 {
219 _canTransact = (SQL_TC_NONE != ret) ?
220 ODBC_TXN_CAPABILITY_TRUE :
221 ODBC_TXN_CAPABILITY_FALSE;
222 }
223 else
224 {
225 Error<SQLHDBC, SQL_HANDLE_DBC> err(_db);
226 _canTransact = ODBC_TXN_CAPABILITY_FALSE;
227 }
228 }
229
230 return ODBC_TXN_CAPABILITY_TRUE == _canTransact;
231}
232
233
234void SessionImpl::setTransactionIsolation(Poco::UInt32 ti)
235{
236#if POCO_PTR_IS_64_BIT
237 Poco::UInt64 isolation = 0;
238#else
239 Poco::UInt32 isolation = 0;
240#endif
241
242 if (ti & Session::TRANSACTION_READ_UNCOMMITTED)
243 isolation |= SQL_TXN_READ_UNCOMMITTED;
244
245 if (ti & Session::TRANSACTION_READ_COMMITTED)
246 isolation |= SQL_TXN_READ_COMMITTED;
247
248 if (ti & Session::TRANSACTION_REPEATABLE_READ)
249 isolation |= SQL_TXN_REPEATABLE_READ;
250
251 if (ti & Session::TRANSACTION_SERIALIZABLE)
252 isolation |= SQL_TXN_SERIALIZABLE;
253
254 checkError(Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)isolation, 0));
255}
256
257
258Poco::UInt32 SessionImpl::getTransactionIsolation()
259{
260 SQLULEN isolation = 0;
261 checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION,
262 &isolation,
263 0,
264 0));
265
266 return transactionIsolation(isolation);
267}
268
269
270bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti)
271{
272 if (isTransaction()) throw InvalidAccessException();
273
274 bool retval = true;
275 Poco::UInt32 old = getTransactionIsolation();
276 try { setTransactionIsolation(ti); }
277 catch (Poco::Exception&) { retval = false; }
278 setTransactionIsolation(old);
279 return retval;
280}
281
282
283Poco::UInt32 SessionImpl::getDefaultTransactionIsolation()
284{
285 SQLUINTEGER isolation = 0;
286 checkError(Poco::Data::ODBC::SQLGetInfo(_db, SQL_DEFAULT_TXN_ISOLATION,
287 &isolation,
288 0,
289 0));
290
291 return transactionIsolation(isolation);
292}
293
294
295Poco::UInt32 SessionImpl::transactionIsolation(SQLULEN isolation)
296{
297 if (0 == isolation)
298 throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)");
299
300 Poco::UInt32 ret = 0;
301
302 if (isolation & SQL_TXN_READ_UNCOMMITTED)
303 ret |= Session::TRANSACTION_READ_UNCOMMITTED;
304
305 if (isolation & SQL_TXN_READ_COMMITTED)
306 ret |= Session::TRANSACTION_READ_COMMITTED;
307
308 if (isolation & SQL_TXN_REPEATABLE_READ)
309 ret |= Session::TRANSACTION_REPEATABLE_READ;
310
311 if (isolation & SQL_TXN_SERIALIZABLE)
312 ret |= Session::TRANSACTION_SERIALIZABLE;
313
314 if (0 == ret)
315 throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)");
316
317 return ret;
318}
319
320
321void SessionImpl::autoCommit(const std::string&, bool val)
322{
323 checkError(Poco::Data::ODBC::SQLSetConnectAttr(_db,
324 SQL_ATTR_AUTOCOMMIT,
325 val ? (SQLPOINTER) SQL_AUTOCOMMIT_ON :
326 (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
327 SQL_IS_UINTEGER), "Failed to set automatic commit.");
328}
329
330
331bool SessionImpl::isAutoCommit(const std::string&)
332{
333 SQLULEN value = 0;
334
335 checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
336 SQL_ATTR_AUTOCOMMIT,
337 &value,
338 0,
339 0));
340
341 return (0 != value);
342}
343
344
345bool SessionImpl::isTransaction()
346{
347 if (!canTransact()) return false;
348
349 SQLULEN value = 0;
350 checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
351 SQL_ATTR_AUTOCOMMIT,
352 &value,
353 0,
354 0));
355
356 if (0 == value) return _inTransaction;
357 else return false;
358}
359
360
361void SessionImpl::begin()
362{
363 if (isAutoCommit())
364 throw InvalidAccessException("Session in auto commit mode.");
365
366 {
367 Poco::FastMutex::ScopedLock l(_mutex);
368
369 if (_inTransaction)
370 throw InvalidAccessException("Transaction in progress.");
371
372 _inTransaction = true;
373 }
374}
375
376
377void SessionImpl::commit()
378{
379 if (!isAutoCommit())
380 checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_COMMIT));
381
382 _inTransaction = false;
383}
384
385
386void SessionImpl::rollback()
387{
388 if (!isAutoCommit())
389 checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_ROLLBACK));
390
391 _inTransaction = false;
392}
393
394
395void SessionImpl::close()
396{
397 if (!isConnected()) return;
398
399 try
400 {
401 commit();
402 }
403 catch (ConnectionException&)
404 {
405 }
406
407 SQLCHAR sqlState[5+1];
408 SQLCHAR sqlErrorMessage[SQL_MAX_MESSAGE_LENGTH];
409 SQLINTEGER nativeErrorCode;
410 SQLSMALLINT sqlErrorMessageLength;
411 unsigned int retryCount = 10;
412 do
413 {
414 SQLRETURN rc = SQLDisconnect(_db);
415 if (SQL_SUCCESS != rc && SQL_SUCCESS_WITH_INFO != rc)
416 {
417 SQLGetDiagRec(SQL_HANDLE_DBC, _db,
418 1,
419 sqlState,
420 &nativeErrorCode,
421 sqlErrorMessage, SQL_MAX_MESSAGE_LENGTH, &sqlErrorMessageLength);
422 if (std::string(reinterpret_cast<const char *>(sqlState)) == "25000"
423 || std::string(reinterpret_cast<const char *>(sqlState)) == "25501")
424 {
425 --retryCount;
426 Poco::Thread::sleep(100);
427 }
428 else
429 {
430 break;
431 }
432 }
433 else
434 {
435 return;
436 }
437 }
438 while(retryCount > 0);
439
440 throw ODBCException
441 (std::string(reinterpret_cast<const char *>(sqlErrorMessage)), nativeErrorCode);
442}
443
444
445int SessionImpl::maxStatementLength()
446{
447 SQLUINTEGER info;
448 SQLRETURN rc = 0;
449 if (Utility::isError(rc = Poco::Data::ODBC::SQLGetInfo(_db,
450 SQL_MAXIMUM_STATEMENT_LENGTH,
451 (SQLPOINTER) &info,
452 0,
453 0)))
454 {
455 throw ConnectionException(_db,
456 "SQLGetInfo(SQL_MAXIMUM_STATEMENT_LENGTH)");
457 }
458
459 return info;
460}
461
462
463} } } // namespace Poco::Data::ODBC
464