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