DCL 3.7.4
Loading...
Searching...
No Matches
IFXConnection.ec
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <sqlstype.h>
4
5#include <ctype.h> // isspace
6#include <string.h> // strlen, strncpy
7
8#include <dcl/Object.h>
9#if __DCL_HAVE_ALLOC_DEBUG
10#undef __DCL_ALLOC_LEVEL
11#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
12#endif
13
14#include <dcl/Numeric.h>
15#include <dcl/Regex.h>
16#include <dcl/SQLCore.h>
17
18#include "IFXConnection.h"
19#include "IFXQuery.h"
20#include "IFXField.h" // for _getServerInfo
21
22#define __TRACE_THIS 0
23#if __TRACE_THIS
24#define __DCL_TRACE0_N __DCL_TRACE0
25#define __DCL_TRACE1_N __DCL_TRACE1
26#define __DCL_TRACE2_N __DCL_TRACE2
27#define __DCL_TRACE3_N __DCL_TRACE3
28#define __DCL_TRACE4_N __DCL_TRACE4
29#else
30#define __DCL_TRACE0_N(fmt)
31#define __DCL_TRACE1_N(fmt, arg)
32#define __DCL_TRACE2_N(fmt, arg1, arg2)
33#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
34#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
35#endif
36
37#undef __THIS_FILE__
38static const char_t __THIS_FILE__[] = __T("dcl/sql/IFXConnection.ec");
39
40__DCL_BEGIN_NAMESPACE
41
42#define __SET_ERROR(_error) \
43 setErrorHandle(_error, 0L, __THIS_FILE__, __LINE__)
44#define __SET_ERROR_HANDLE(_SQLCODE) \
45 setErrorHandle(SQL::eServerError, _SQLCODE, __THIS_FILE__, __LINE__)
46#define __SET_ERROR_MSG(_message) \
47 setErrorMessage(_message, __THIS_FILE__, __LINE__)
48
49IMPLEMENT_CLASSINFO(IFXConnection, SQL::Connection)
50
51IFXConnection::IFXConnection(const wchar_t* _serverTitle)
52 : Connection(_serverTitle)
53{
54}
55
56IFXConnection::~IFXConnection()
57{
58 if (!__connectionID.isEmpty()) {
59 __DCL_TRACE0_N(L"Warning!! The connection was not closed\n");
60 close();
61 }
62}
63
64void IFXConnection::destroy()
65{
66 delete this;
67}
68
69/*
70_conns에 가능한 프로퍼티
71 USER,
72 PASSWORD,
73 SERVER, -- host name
74 DATABASE
75*/
76
77void IFXConnection::reset()
78{
79 EXEC SQL BEGIN DECLARE SECTION;
80 char* connID = (_CONST char*) __connectionID.data();
81 EXEC SQL END DECLARE SECTION;
82
83 EXEC SQL SET CONNECTION :connID;
84
85 EXEC SQL BEGIN WORK;
86 if (SQLCODE == 0) {
87 Connection::__canTransact = true;
88 EXEC SQL ROLLBACK WORK;
89 }
90 else {
91 Connection::__canTransact = false;
92 }
93
94 __UNSET_STATE(stInTransaction);
95}
96
97bool IFXConnection::__open(const char* _conns, size_t _connlen)
98{
99 ListedByteStringToByteStringMap map;
100 Connection::splitConnStr(_conns, _connlen, map);
101
102 ByteString _SERVER = map["SERVER"];
103 ByteString _USER = map["USER"];
104 ByteString _PASSWORD = map["PASSWORD"];
105 ByteString _DATABASE = map["DATABASE"];
106 ByteString _TARGET;
107
108 {
109 ByteStringBuilder sb = _DATABASE;
110 if (!_SERVER.isEmpty()) {
111 sb.append("@").append(_SERVER);
112 }
113 _TARGET = sb.toByteString();
114 __DCL_TRACE1_N(L"TARGET [%hs]\n", _TARGET.data());
115 }
116
117 ByteString connectionID = ByteString::format("conn_%zx", (size_t)this);
118
119 EXEC SQL BEGIN DECLARE SECTION;
120 char* user = (_CONST char*) _USER.data();
121 char* pass = (_CONST char*) _PASSWORD.data();
122 char* target = (_CONST char*) _TARGET.data();
123 char* connID = (_CONST char*) connectionID.data();
124 EXEC SQL END DECLARE SECTION;
125
126 EXEC SQL CONNECT TO :target AS :connID
127 USER :user USING :pass
128 WITH CONCURRENT TRANSACTION;
129
130 if (SQLCODE < 0) {
131 __SET_ERROR_HANDLE(SQLCODE);
132 return false;
133 }
134 __connectionID = connectionID;
135
136 if (!_DATABASE.isEmpty()) {
137 reset();
138 }
139 return true;
140}
141
142bool IFXConnection::__close()
143{
144 if (__connectionID.isEmpty()) {
145 __SET_ERROR(SQL::eNotConnected);
146 return false;
147 }
148
149 EXEC SQL BEGIN DECLARE SECTION;
150 char* connID = (_CONST char*) __connectionID.data();
151 EXEC SQL END DECLARE SECTION;
152
153 EXEC SQL DISCONNECT :connID;
154 if (SQLCODE < 0) {
155 __SET_ERROR_HANDLE(SQLCODE);
156 return false;
157 }
158
159 __connectionID.clear();
160 return true;
161}
162
163enum StmtType {
164 StmtOther,
165 StmtTransBegin,
166 StmtTransEnd,
167 StmtDatabase
168};
169
170typedef struct {
171 StmtType type;
172 const char* pattern;
173} STMT_PATTERN;
174
175static STMT_PATTERN sp[] = {
176 { StmtTransBegin, "BEGIN" },
177 { StmtTransEnd, "COMMIT|ROLLBACK" },
178 { StmtDatabase, "DATABASE" },
179 { StmtOther, NULL }
180};
181
182static StmtType __GetStmtType(const char* _sql)
183{
184 for(size_t i = 0; sp[i].type != StmtOther; i++) {
185 try {
186 if (Regex::test(sp[i].pattern, _sql, true))
187 return sp[i].type;
188 }
189 catch(Exception* e) {
190 e->destroy();
191 }
192 }
193 return StmtOther;
194}
195
196bool IFXConnection::__execute(const char* _sql, size_t _sqllen)
197{
198 EXEC SQL BEGIN DECLARE SECTION;
199 char* connID = (_CONST char*) __connectionID.data();
200 char* pszSqlStatement = (_CONST char*) _sql;
201 EXEC SQL END DECLARE SECTION;
202
203 EXEC SQL SET CONNECTION :connID;
204 if (SQLCODE < 0) {
205 __SET_ERROR_HANDLE(SQLCODE);
206 return false;
207 }
208
209 EXEC SQL EXECUTE IMMEDIATE :pszSqlStatement;
210 if (SQLCODE < 0) {
211 __SET_ERROR_HANDLE(SQLCODE);
212 return false;
213 }
214
215 switch(__GetStmtType(_sql)) {
216 case StmtTransBegin :
217 __SET_STATE(stInTransaction);
218 break;
219 case StmtTransEnd :
220 __UNSET_STATE(stInTransaction);
221 break;
222 case StmtDatabase :
223 reset();
224 break;
225 case StmtOther :
226 default :
227 ;
228 }
229
230 return true;
231}
232
233bool IFXConnection::__startTrans()
234{
235 EXEC SQL BEGIN DECLARE SECTION;
236 char* connID = (_CONST char*) __connectionID.data();
237 EXEC SQL END DECLARE SECTION;
238
239 EXEC SQL SET CONNECTION :connID;
240 if (SQLCODE < 0) {
241 __SET_ERROR_HANDLE(SQLCODE);
242 return false;
243 }
244
245 EXEC SQL BEGIN WORK;
246 if (SQLCODE < 0) {
247 __SET_ERROR_HANDLE(SQLCODE);
248 return false;
249 }
250
251 __SET_STATE(stInTransaction);
252 return true;
253}
254
255bool IFXConnection::__commitTrans()
256{
257 EXEC SQL BEGIN DECLARE SECTION;
258 char* connID = (_CONST char*) __connectionID.data();
259 EXEC SQL END DECLARE SECTION;
260
261 EXEC SQL SET CONNECTION :connID;
262 if (SQLCODE < 0) {
263 __SET_ERROR_HANDLE(SQLCODE);
264 return false;
265 }
266
267 EXEC SQL COMMIT WORK;
268 if (SQLCODE < 0) {
269 __SET_ERROR_HANDLE(SQLCODE);
270 return false;
271 }
272
273 __UNSET_STATE(stInTransaction);
274 return true;
275}
276
277bool IFXConnection::__rollbackTrans()
278{
279 EXEC SQL BEGIN DECLARE SECTION;
280 char* connID = (_CONST char*) __connectionID.data();
281 EXEC SQL END DECLARE SECTION;
282
283 EXEC SQL SET CONNECTION :connID;
284 if (SQLCODE < 0) {
285 __SET_ERROR_HANDLE(SQLCODE);
286 return false;
287 }
288
289 EXEC SQL ROLLBACK WORK;
290 if (SQLCODE < 0) {
291 __SET_ERROR_HANDLE(SQLCODE);
292 return false;
293 }
294
295 __UNSET_STATE(stInTransaction);
296 return true;
297}
298
299bool IFXConnection::__createQueryInstance(SQL::Query** _queryHandleOut)
300{
301 __DCL_ASSERT(_queryHandleOut != NULL);
302
303 SQL::Query* pNewQuery = new IFXQuery(this);
304 if (!pNewQuery) {
305 __SET_ERROR(SQL::eOutOfMemory);
306 return false;
307 }
308
309 *_queryHandleOut = pNewQuery;
310 return true;
311}
312
313void IFXConnection::setErrorHandle(
314 SQL::Error _error, long _SQLCODE,
315 const wchar_t* _filename, int _line
316)
317{
318 Connection::setErrorStatus(_error, _filename, _line);
319 if (_SQLCODE == 0) {
320 __lastErrorMessage.clear();
321 return;
322 }
323
324 __DCL_ASSERT(_SQLCODE == sqlca.sqlcode);
325
326 ByteStringBuilder sb;
327 sb.format("SQLCODE(%d) ", _SQLCODE);
328
329 char buf[1024];
330 mint actual = 0;
331 mint r = rgetlmsg(_SQLCODE, buf, sizeof(buf), &actual);
332 if (!r) {
333 while (actual > 0) {
334 if (!isspace(buf[actual - 1]))
335 break;
336 actual--;
337 }
338 buf[actual] = '\0';
339 sb.format(buf, sqlca.sqlerrm);
340 }
341 else {
342 sb.format("(%d)", r);
343 switch (r) {
344 case -1227:
345 sb.append("Message file not found");
346 break;
347 case -1228:
348 sb.append("Message number not found in message file");
349 break;
350 case -1231:
351 sb.append("Cannot seek within messge file");
352 break;
353 case -1232:
354 sb.append("Message buffer too small");
355 break;
356 default:
357 sb.append("Unkndown");
358 }
359 }
360 __lastErrorMessage = sb.toByteString();
361}
362
363bool IFXConnection::__getErrorMessage(char* _buf, size_t* _buflen)
364{
365 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
366 if (__lastErrorMessage.length() < *_buflen) {
367 *_buflen = __lastErrorMessage.length();
368 *(_buf + *_buflen) = '\0';
369 }
370 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
371 return true;
372}
373
374bool IFXConnection::__getServerInfo(char* _buf, size_t* _buflen)
375{
376 IFXQuery* query = new IFXQuery(this);
377 if (query == NULL) {
378 __SET_ERROR(SQL::eOutOfMemory);
379 return false;
380 }
381
382 bool localTrans = false;
383 if (!inState(SQL::Connection::stInTransaction)) {
384 __startTrans();
385 localTrans = true;
386 }
387
388 const char* _sql =
389 "SELECT DBINFO('version', 'full')"
390 "\n FROM systables WHERE tabid = 1";
391 for ( ; ; ) {
392 if (!query->prepare(_sql, ByteString::length(_sql), 0))
393 break;
394
395 if (!query->execute())
396 break;
397
398 if (!query->fetch())
399 break;
400
401 if (query->eof()) {
402 __SET_ERROR(SQL::eNotAvailable);
403 break;
404 }
405
406 IFXField* field = NULL;
407 if (!query->__getField(0, (SQL::Field**)&field))
408 break;
409
410 size_t size = (size_t)-1;
411 if (!field->__getDataSize(&size, false) || size == (size_t)-1) {
412 __SET_ERROR(SQL::eNotAvailable);
413 break;
414 }
415
416 if (!field->__getData(_buf, _buflen, SQL::typeText))
417 break;
418
419 // *_buflen에 '\0'이 포함되어 있다.
420 *_buflen = ByteString::length(_buf, *_buflen);
421
422 query->__destroy();
423 query = NULL;
424
425 if (localTrans) {
426 __commitTrans();
427 }
428 return true;
429 }
430
431 if (query) {
432 query->__destroy();
433 }
434
435 if (localTrans) {
436 __rollbackTrans();
437 }
438 return false;
439}
440
441__DCL_END_NAMESPACE