DCL 3.7.4
Loading...
Searching...
No Matches
ODBCConnection.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <string.h> // strlen, strncpy
4
5#ifdef _MSC_VER
6#include <windows.h>
7#endif
8#include <sql.h>
9#include <sqlext.h>
10
11#include <dcl/Object.h>
12#if __DCL_HAVE_ALLOC_DEBUG
13#undef __DCL_ALLOC_LEVEL
14#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
15#endif
16
17#include <dcl/size_t.h>
18#include <dcl/Charset.h>
19#include <dcl/SQLCore.h>
20
21#include "ODBCConnection.h"
22#include "ODBCQuery.h"
23
24#define __TRACE_THIS 0
25#if __TRACE_THIS
26#define __DCL_TRACE0_N __DCL_TRACE0
27#define __DCL_TRACE1_N __DCL_TRACE1
28#define __DCL_TRACE2_N __DCL_TRACE2
29#define __DCL_TRACE3_N __DCL_TRACE3
30#define __DCL_TRACE4_N __DCL_TRACE4
31#else
32#define __DCL_TRACE0_N(fmt)
33#define __DCL_TRACE1_N(fmt, arg)
34#define __DCL_TRACE2_N(fmt, arg1, arg2)
35#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
36#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
37#endif
38
39#undef __THIS_FILE__
40static const char_t __THIS_FILE__[] = __T("dcl/sql/ODBCConnection.cpp");
41
42__DCL_BEGIN_NAMESPACE
43
44#define __SET_ERROR(_error) \
45 setErrorStatus(_error, __THIS_FILE__, __LINE__)
46#define __SET_ERROR_MSG(_msg) \
47 setErrorMessage(_msg, __THIS_FILE__, __LINE__)
48#define __SET_ERROR_HANDLE(_rc, _htype, _handle) \
49 setErrorHandle(_rc, _htype, _handle, __THIS_FILE__, __LINE__)
50#define __SET_INFO_HANDLE(_rc, _htype, _handle) \
51 if (_rc == SQL_SUCCESS_WITH_INFO) { \
52 setErrorHandle(_rc, _htype, _handle, __THIS_FILE__, __LINE__); \
53 }
54
56
57ODBCConnection::ODBCConnection(const wchar_t* _serverTitle)
58 : Connection(_serverTitle)
59{
60 Connection::__canTransact = true;
61 __henv = NULL;
62 __hdbc = NULL;
63
64 __IS_MS_SS = false;
65}
66
68{
69 if (__hdbc) {
70 __DCL_TRACE0_N(L"Warning!! The connection was not closed\n");
71 close();
72 }
73
74 if (__henv) {
75 SQLRETURN rc_ = SQLFreeHandle(SQL_HANDLE_ENV, __henv);
76 if (rc_ != SQL_SUCCESS) {
77 __DCL_TRACE1_N(L"SQLFreeHandle(ENV) Fail! [%d]\n", rc_);
78 }
79 __henv = NULL;
80 }
81}
82
84{
85 delete this;
86}
87
88bool ODBCConnection::__open(const char* _cons, size_t _conslen)
89{
90 SQLRETURN rc = SQL_SUCCESS;
91 if (!__henv) {
92 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &__henv);
93 if (rc != SQL_SUCCESS) {
95 ByteString::format("Unable to allocate an environment handle [%d]",
96 rc));
97 return false;
98 }
99
100 rc = SQLSetEnvAttr(__henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
101 if (rc != SQL_SUCCESS) {
102 __SET_ERROR_HANDLE(rc, SQL_HANDLE_ENV, __henv);
103 return false;
104 }
105 }
106
107 __DCL_ASSERT_HANDLE(__hdbc == NULL);
108 rc = SQLAllocHandle(SQL_HANDLE_DBC, __henv, &__hdbc);
109 if (rc != SQL_SUCCESS) {
110 __SET_ERROR_HANDLE(rc, SQL_HANDLE_ENV, __henv);
111 return false;
112 }
113
114 for ( ; ; ) {
115 SQLSMALLINT length2 = 0;
116 rc = SQLDriverConnectA(__hdbc, NULL,
117 (SQLCHAR*)_cons, (SQLSMALLINT)_conslen,
118 NULL, 0, &length2, SQL_DRIVER_NOPROMPT
119 );
120 if (!SQL_SUCCEEDED(rc)) {
121 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
122 break;
123 }
124 __SET_INFO_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
125
126#if defined(__WINNT__) && !defined(strncasecmp)
127#define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n)
128#endif
129 char buf[128];
130 SQLSMALLINT len = sizeof(buf);
131 rc = SQLGetInfoA(__hdbc, SQL_DBMS_NAME, buf, len, &len);
132 if (rc != SQL_SUCCESS) {
133 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
134 return false;
135 }
136 __IS_MS_SS = strncasecmp(buf, "Microsoft SQL Server", __MIN(9, len)) == 0;
137 __DCL_TRACE3_N(L"[%hs][%d] [%d]\n", buf, len, __IS_MS_SS);
138
139 rc = SQLSetConnectAttrA(__hdbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
140 if (rc != SQL_SUCCESS) {
141 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
142 break;
143 }
144
145 #if __TRACE_THIS
146 SQLUINTEGER GetDataSupport = 0;
147 SQLSMALLINT length;
148
149 rc = SQLGetInfoA(__hdbc, SQL_GETDATA_EXTENSIONS,
150 &GetDataSupport, sizeof(GetDataSupport), &length);
151 __DCL_TRACE3_N(L"[%04x] AC[%04x] AO[%04x]\n",
152 GetDataSupport,
153 GetDataSupport & SQL_GD_ANY_COLUMN,
154 GetDataSupport & SQL_GD_ANY_ORDER
155 );
156 __DCL_TRACE3_N(L"BL[%04x] BO[%04x] OP[%04x]\n",
157 GetDataSupport & SQL_GD_BLOCK,
158 GetDataSupport & SQL_GD_BOUND,
159 GetDataSupport & SQL_GD_OUTPUT_PARAMS
160 );
161 #endif
162 return true;
163 }
164
165 // Connect Failed
166 SQLRETURN rc_ = SQLFreeHandle(SQL_HANDLE_DBC, __hdbc);
167 if (rc_ != SQL_SUCCESS) {
168 __DCL_TRACE1_N(L"SQLFreeHandle(DBC) Fail! [%d]\n", rc_);
169 }
170 __hdbc = NULL;
171 return false;
172}
173
175{
176 if (!__hdbc) {
178 return false;
179 }
180
181 SQLRETURN rc = SQL_SUCCESS;
182 rc = SQLDisconnect(__hdbc);
183 if (rc != SQL_SUCCESS) {
184 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
185 }
186
187 SQLRETURN rc_ = SQLFreeHandle(SQL_HANDLE_DBC, __hdbc);
188 if (rc_ != SQL_SUCCESS) {
189 __DCL_TRACE1_N(L"SQLFreeHandle(DBC) Fail! [%d]\n", rc_);
190 }
191 __hdbc = NULL;
192
193 return rc == SQL_SUCCESS;
194}
195
196bool ODBCConnection::__execute(const char* _sql, size_t _sqllen)
197{
198 SQLHSTMT hstmt = NULL;
199 SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, __hdbc, &hstmt);
200 if (rc != SQL_SUCCESS) {
201 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
202 return false;
203 }
204
205 rc = SQLExecDirectA(hstmt, (SQLCHAR*) _sql, (SQLINTEGER)_sqllen);
206 if (!SQL_SUCCEEDED(rc)) {
207 __SET_ERROR_HANDLE(rc, SQL_HANDLE_STMT, hstmt);
208 }
209 __SET_INFO_HANDLE(rc, SQL_HANDLE_STMT, hstmt);
210
211 SQLRETURN rc_ = SQLFreeStmt(hstmt, SQL_CLOSE);
212 if (rc_ != SQL_SUCCESS) {
213 __DCL_TRACE1_N(L"SQLFreeStmt(STMT) Fail! [%d]\n", rc_);
214 }
215
216 rc_ = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
217 if (rc_ != SQL_SUCCESS) {
218 __DCL_TRACE1_N(L"SQLFreeHandle(STMT) Fail! [%d]\n", rc_);
219 }
220 hstmt = NULL;
221
222 return rc == SQL_SUCCESS;
223}
224
226{
227 __SET_STATE(Connection::stInTransaction);
228 return true;
229}
230
232{
233 __UNSET_STATE(Connection::stInTransaction);
234 SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, __hdbc, SQL_COMMIT);
235 if (rc != SQL_SUCCESS) {
236 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
237 return false;
238 }
239
240 return true;
241}
242
244{
245 __UNSET_STATE(Connection::stInTransaction);
246 SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, __hdbc, SQL_ROLLBACK);
247 if (rc != SQL_SUCCESS) {
248 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
249 return false;
250 }
251
252 return true;
253}
254
256{
257 __DCL_ASSERT(_ppQuery != NULL);
258
259 ODBCQuery* pNewQuery = new ODBCQuery(this);
260 if (!pNewQuery) {
262 return false;
263 }
264
265 *_ppQuery = pNewQuery;
266 return true;
267}
268
269bool ODBCConnection::__getErrorMessage(char* _buf, size_t* _buflen)
270{
271 if (__lastErrorMessage.length() < *_buflen) {
272 *_buflen = __lastErrorMessage.length();
273 *(_buf + *_buflen) = '\0';
274 }
275 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
276 __lastErrorMessage.clear();
277 return true;
278}
279
280bool ODBCConnection::__getServerInfo(char* _buf, size_t* _buflen)
281{
282#if 0 && defined(__WINNT__)
283 StringBuilder sb;
284 WCHAR buf[1024];
285 SQLSMALLINT length;
286
287 SQLRETURN rc = SQLGetInfoW(__hdbc, SQL_DBMS_NAME, buf, __countof(buf, WCHAR) , &length);
288 if (rc != SQL_SUCCESS) {
289 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
290 return false;
291 }
292 sb.append(buf, length / sizeof(WCHAR));
293
294 rc = SQLGetInfoW(__hdbc, SQL_DBMS_VER, buf, __countof(buf, WCHAR) , &length);
295 if (rc != SQL_SUCCESS) {
296 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
297 return false;
298 }
299 sb.append(L' ');
300 sb.append(buf, length / sizeof(WCHAR));
301
302 ByteString s = UTF8Encoder::encode(sb.toString());
303 if (s.length() < *_buflen) {
304 *_buflen = s.length();
305 }
306 strncpy(_buf, s.data(), *_buflen);
307 return true;
308#else
309 SQLSMALLINT size = (SQLSMALLINT) __MIN(*_buflen, SHRT_MAX);
310 SQLSMALLINT offset = 0;
311 SQLSMALLINT length;
312 SQLRETURN rc = SQL_SUCCESS;
313
314 rc = SQLGetInfoA(__hdbc, SQL_DBMS_NAME,
315 _buf + offset, size - offset, &length);
316 if (rc != SQL_SUCCESS) {
317 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
318 return false;
319 }
320
321 if ((offset + length) >= size) {
322 *_buflen = size;
323 return true;
324 }
325
326 offset += length;
327 *(_buf + offset) = ' ';
328 offset++;
329
330 rc = SQLGetInfoA(__hdbc, SQL_DBMS_VER,
331 _buf + offset, size - offset, &length);
332 if (rc != SQL_SUCCESS) {
333 __SET_ERROR_HANDLE(rc, SQL_HANDLE_DBC, __hdbc);
334 return false;
335 }
336
337 if ((offset + length) >= size) {
338 *_buflen = size;
339 return true;
340 }
341
342 *_buflen = offset + length;
343 return true;
344#endif
345}
346
348 SQLRETURN _rc,
349 SQLSMALLINT _htype,
350 SQLHANDLE _handle,
351 const wchar_t* _filename,
352 int _line
353)
354{
355 Connection::setErrorStatus(SQL::eServerError, _filename, _line);
356
357 if (_rc == SQL_SUCCESS_WITH_INFO || _rc == SQL_ERROR) {
359 _htype == SQL_HANDLE_ENV
360 || _htype == SQL_HANDLE_DBC
361 || _htype == SQL_HANDLE_STMT
362 || _htype == SQL_HANDLE_DESC
363 // || _htype == SQL_HANDLE_DBC_INFO_TOKEN
364 );
365 __DCL_ASSERT(_handle != NULL);
366
367#if 0 && defined(__WINNT__)
368 /*
369 * sizeof(WCHAR) == 2 인 Windows에서만 사용할 수 있다.
370 * Linux에서 WCHAR은 기본적으로 signed short로 wchar_t가 아니다.
371 * 이 코드는 LANG=C.UTF-4 환경변수가 적용되지 않는 Windows에서만 적용한다.
372 */
373 StringBuilder sb;
374 SQLSMALLINT RecNumber = 0;
375 SQLINTEGER NativeError;
376 WCHAR SQLState[SQL_SQLSTATE_SIZE + 1];
377 WCHAR MessageText[1024];
378 SQLSMALLINT MessageTextLength;
379 SQLRETURN rc;
380 while ((rc = SQLGetDiagRecW(
381 _htype, _handle, ++RecNumber, SQLState, &NativeError,
382 MessageText, __countof(MessageText, WCHAR), &MessageTextLength
383 )) == SQL_SUCCESS) {
384 if (sb.length() > 0) {
385 sb.append('\n');
386 }
387 sb.format(L"[%d][%ls]", RecNumber, SQLState);
388 sb.append(MessageText, (size_t) MessageTextLength);
389 }
390 //__DCL_TRACE1_N(L"rc[%d]\n", rc);
391 String s = sb.toString();
392 #if __TRACE_THIS
393 if (_rc == SQL_SUCCESS_WITH_INFO) {
394 __DCL_TRACE1_N(L"SQL_SUCCESS_WITH_INFO:\n %ls\n",
395 s.replace(L"\n", L"\n ").data());
396 }
397 #endif
398 try {
399 __lastErrorMessage = UTF8Encoder::encode(s);
400 }
401 catch (CharsetConvertException* _e) {
402 __DCL_TRACE1(L"Fatal!! [%ls]\n", _e->toString().data());
403 _e->destroy();
404 }
405#else
406 ByteStringBuilder sb;
407 SQLSMALLINT RecNumber = 0;
408 SQLINTEGER NativeError;
409 SQLCHAR SQLState[SQL_SQLSTATE_SIZE + 1];
410 SQLCHAR ErrorMsg[1024];
411 SQLSMALLINT ErrorMsgLen;
412 SQLRETURN rc;
413 while ((rc = SQLGetDiagRecA(
414 _htype, _handle, ++RecNumber, SQLState, &NativeError,
415 ErrorMsg, __countof(ErrorMsg, SQLCHAR), &ErrorMsgLen
416 )) == SQL_SUCCESS) {
417 if (sb.length() > 0) {
418 sb.append('\n');
419 }
420 sb.format("[%d][%hs]", RecNumber, SQLState);
421 sb.append((const char*)ErrorMsg, (size_t)ErrorMsgLen);
422 }
423 __DCL_ASSERT(rc == SQL_NO_DATA);
424 ByteString s;
425#ifdef __WINNT__
426 /*
427 * Windows에서 LANG=C.UTF-4 환경변수가 적용되지 않으므로,
428 * wchar_t로 변환 후 UTF-8로 다시 변환한다.
429 */
430 try {
431 String ws = LocaleDecoder::decode(sb.toByteString());
432 s = UTF8Encoder::encode(ws);
433 #if __TRACE_THIS
434 if (_rc == SQL_SUCCESS_WITH_INFO) {
435 __DCL_TRACE1_N(L"SQL_SUCCESS_WITH_INFO:\n %ls\n",
436 ws.replace(L"\n", L"\n ").data());
437 }
438 #endif
439 }
440 catch (CharsetConvertException* _e) {
441 __DCL_TRACE1(L"Fatal!! [%ls]\n", _e->toString().data());
442 _e->destroy();
443 }
444#else
445 s = sb.toByteString();
446 #if __TRACE_THIS
447 if (_rc == SQL_SUCCESS_WITH_INFO) {
448 __DCL_TRACE1_N(L"SQL_SUCCESS_WITH_INFO:\n %hs\n",
449 s.replace("\n", "\n ").data());
450 }
451 #endif
452#endif
453 __lastErrorMessage = s;
454#endif
455 }
456 else {
457 #define CASE_STR(rc) case rc: __lastErrorMessage = #rc; break;
458 switch (_rc) {
459 CASE_STR(SQL_SUCCESS)
460 CASE_STR(SQL_SUCCESS_WITH_INFO)
462 CASE_STR(SQL_NO_DATA)
463 CASE_STR(SQL_INVALID_HANDLE)
464 default:
465 __DCL_ASSERT(false);
466 __lastErrorMessage = ByteString::format("SQLRETURN[%d] mabe bug!!", _rc);
467 }
468 }
469}
470
471__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:312
#define __countof(array, type)
Definition Config.h:336
wchar_t char_t
Definition Config.h:247
#define __DCL_TRACE1_N(fmt, arg)
#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
#define __SET_ERROR_MSG(_message)
#define __DCL_TRACE0_N(fmt)
#define __SET_ERROR_HANDLE(_SQLCODE)
#define __SET_INFO_HANDLE(_rc, _htype, _handle)
#define CASE_STR(rc)
#define __DCL_TRACE1(fmt, arg1)
Definition Object.h:399
#define __DCL_ASSERT(expr)
Definition Object.h:394
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:245
#define __T(str)
Definition Object.h:60
#define __DCL_ASSERT_HANDLE(expr)
Definition Object.h:408
__DCL_BEGIN_NAMESPACE struct __SQL_ERROR SQL_ERROR
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:149
#define __UNSET_STATE(state)
Definition SQLCore.h:483
#define __SET_STATE(state)
Definition SQLCore.h:482
virtual void destroy()
Definition Exception.cpp:74
virtual bool __getErrorMessage(char *_buf, size_t *_buflen)
ODBCConnection(const wchar_t *_serverTitle)
virtual bool __open(const char *_cons, size_t _conslen)
virtual bool __execute(const char *_sql, size_t _sqllen)
void setErrorHandle(SQLRETURN _rc, SQLSMALLINT _htype, SQLHANDLE _handle, const wchar_t *_filename, int _line)
virtual bool __commitTrans()
virtual bool __close()
virtual bool __createQueryInstance(SQL::Query **_queryHandleOut)
virtual bool __startTrans()
virtual void destroy()
virtual bool __getServerInfo(char *_buf, size_t *_buflen)
virtual ~ODBCConnection()
virtual bool __rollbackTrans()
_PROTECTED const wchar_t * _filename
Definition SQLCore.h:439
_PROTECTED const wchar_t int _line
Definition SQLCore.h:441
@ eOutOfMemory
Definition SQLCore.h:24
@ eServerError
Definition SQLCore.h:21
@ eNotConnected
Definition SQLCore.h:33
size_t __MIN(size_t x, size_t y)
Definition size_t.h:27