DCL 4.0
Loading...
Searching...
No Matches
IFXQuery.ec
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <sqlda.h>
4#include <sqlstype.h>
5#include <sqltypes.h>
6
7#include <stdlib.h> // malloc, free
8#include <string.h> // memset
9
10#include <dcl/Object.h>
11#if __DCL_HAVE_ALLOC_DEBUG
12#undef __DCL_ALLOC_LEVEL
13#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
14#endif
15
16#include <dcl/Numeric.h>
17#include <dcl/SQLCore.h>
18
19#include "IFXConnection.h"
20#include "IFXQuery.h"
21#include "IFXField.h"
22#include "IFXParam.h"
23#include "IFXUtils.h" // for void ndebug_free(void* _pv)
24
25#define __TRACE_THIS 0
26#if __TRACE_THIS
27#define __DCL_TRACE0_N __DCL_TRACE0
28#define __DCL_TRACE1_N __DCL_TRACE1
29#define __DCL_TRACE2_N __DCL_TRACE2
30#define __DCL_TRACE3_N __DCL_TRACE3
31#define __DCL_TRACE4_N __DCL_TRACE4
32#else
33#define __DCL_TRACE0_N(fmt)
34#define __DCL_TRACE1_N(fmt, arg)
35#define __DCL_TRACE2_N(fmt, arg1, arg2)
36#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
37#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
38#endif
39
40#undef __THIS_FILE__
41static const wchar_t __THIS_FILE__[] = __T("dcl/sql/IFXQuery.ec");
42
43__DCL_BEGIN_NAMESPACE
44
45IMPLEMENT_CLASSINFO(IFXQuery, SQL::Query)
46
47#ifdef __DCL_DEBUG
48#define __SQ_NAME(_sq) case _sq: return L ## #_sq;
49
50static const wchar_t* __STMT_STRING(int _sq)
51{
52 switch (_sq) {
53 __SQ_NAME(SQ_SELECT)
54 __SQ_NAME(SQ_UPDATE)
55 __SQ_NAME(SQ_DELETE)
56 __SQ_NAME(SQ_INSERT)
57 __SQ_NAME(SQ_EXECPROC)
58 }
59 return L"SQ_OTHER..";
60}
61#endif
62
63IFXQuery::IFXQuery(IFXConnection* pConnection)
64 : Query(pConnection)
65{
66 ByteString strID;
67 strID = ByteString::format("%p", this);
68
69 __statementID = "stmt_" + strID;
70 __cursorID = "cursor_" + strID;
71
72#if __USE_STMT_TYPE
73 __stmtType = __SQ_UNKNOWN;
74#endif
75 __inSQLDA = NULL;
76 __outSQLDA = NULL;
77 __outBuffer = NULL;
78
79 __cursorOpened = false;
80 __cursorDeclared = false;
81
82 __fields = NULL;
83 __params = NULL;
84}
85
86IFXQuery::~IFXQuery()
87{
88#ifdef __DCL_DEBUG
89 if (!reset()) {
90 ByteString s;
91 size_t n = 512;
92 ByteBuffer* buf = ByteBuffer::create(n);
93 bool b = conn()->__getErrorMessage(buf->data(), &n);
94 if (b) {
95 buf->__dataLength = n;
96 s = buf;
97 }
98 buf->release();
99
100 if (b) {
101 __DCL_TRACE1(__T("Warning! %s\n"), s.data());
102 }
103 else {
104 __DCL_TRACE0(__T("Warning! Query reset error\n"));
105 }
106 }
107#else
108 (void)reset();
109#endif
110}
111
112void IFXQuery::__destroy()
113{
114// cerr << "IFXQuery::destory\n";
115 delete this;
116}
117
118bool IFXQuery::reset()
119{
120 EXEC SQL BEGIN DECLARE SECTION;
121 char* pszConnectionID = conn()->connectionID();
122 char* pszStatementID = (_CONST char*)__statementID.data();
123 char* pszCursorID = (_CONST char*)__cursorID.data();
124 EXEC SQL END DECLARE SECTION;
125
126 EXEC SQL SET CONNECTION :pszConnectionID;
127 if (SQLCODE < 0) {
128 __SET_ERROR_SQLCODE(SQLCODE);
129 return false;
130 }
131 __DCL_ASSERT(SQLCODE == 0);
132
133 if (__cursorOpened) {
134 EXEC SQL CLOSE :pszCursorID;
135 __cursorOpened = false;
136 }
137
138 if (__cursorDeclared) {
139 EXEC SQL FREE :pszCursorID;
140 __cursorDeclared = false;
141 }
142
143#if __USE_STMT_TYPE
144 if (__stmtType != __SQ_UNKNOWN) {
145 EXEC SQL FREE :pszStatementID;
146 __stmtType = __SQ_UNKNOWN;
147 }
148#else
149 if (__outSQLDA || __inSQLDA) {
150 EXEC SQL FREE : pszStatementID;
151 }
152#endif
153
154 Query::__eof = true;
155 Query::__affectedRows = -1;
156
157 // clear fields
158 if (__fields) {
159 __DCL_ASSERT(Query::__fieldCount > 0);
160 delete[] __fields;
161 __fields = NULL;
162 Query::__fieldCount = 0;
163 }
164
165 // clear binds
166 if (__params) {
167 __DCL_ASSERT(Query::__paramCount > 0);
168 delete[] __params;
169 __params = NULL;
170 Query::__paramCount = 0;
171 }
172
173 if (__outBuffer) {
174 free(__outBuffer);
175 __outBuffer = NULL;
176 }
177
178 if (__outSQLDA) {
179 // __outSQLDA는 Informix API에서 할당됨.
180 ndebug_free(__outSQLDA);
181 __outSQLDA = NULL;
182 }
183
184 if (__inSQLDA) {
185 ndebug_free(__inSQLDA);
186 __inSQLDA = NULL;
187 }
188
189 return true;
190}
191
192bool IFXQuery::initFields()
193{
194 __DCL_ASSERT((Query::__fieldCount == 0)
195 && (__fields == NULL)
196 && (__outBuffer == NULL)
197 && (__outSQLDA != NULL)
198 && (__outSQLDA->sqld > 0));
199
200 Query::__fieldCount = __outSQLDA->sqld;
201 __fields = new IFXField[Query::__fieldCount];
202 if (__fields == NULL) {
203 __SET_ERROR(SQL::eOutOfMemory);
204 return false;
205 }
206
207 mintptr offset = 0;
208 // 레코드 버퍼 크기 계산
209 ifx_sqlvar_t* sqlvar = __outSQLDA->sqlvar;
210 for(size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
211 offset = rtypalign(offset, sqlvar->sqltype);
212 mint msize = rtypmsize(sqlvar->sqltype, sqlvar->sqllen);
213 __DCL_TRACE4_N(L"[%10hs] sqllen[%4d] msize[%4d] offset[%4d]\n",
214 rtypname(sqlvar->sqltype), sqlvar->sqllen,
215 msize, offset);
216 switch (sqlvar->sqltype & SQLTYPE) {
217 case SQLDECIMAL:
218 case SQLMONEY:
219 case SQLDTIME:
220 case SQLINTERVAL:
221 break;
222 default:
223 sqlvar->sqllen = msize;
224 }
225 offset += msize;
226 /*
227 SQLTYPE sqllen rtypmsize
228 ===========================================
229 SQLCHAR 서버컬럼최대길이 sqllen + 1
230 SQLVCHAR '\0'을 포함한 버퍼의 크기
231 SQLNCHAR
232 SQLNVCHAR
233 -------------------------------------------
234 SQLDECIMAL DECIMAL(p,s) sizeof(dec_t)
235 SQLMONEY 의 p,s의미
236 -------------------------------------------
237 SQLDTIME qualifier sizeof(dtime_t)
238 SQLINTERVAL qualifier sizeof(intrvl_t)
239 */
240 }
241
242 if (offset > 0) {
243 __outBuffer = (char*)malloc(offset);
244 __DCL_ASSERT((size_t)__outBuffer % sizeof(void*) == 0);
245 if (__outBuffer == NULL) {
246 __SET_ERROR(SQL::eOutOfMemory);
247 return false;
248 }
249 memset(__outBuffer, 0, offset);
250 }
251
252 sqlvar = __outSQLDA->sqlvar;
253 offset = 0;
254 for(size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
255 offset = rtypalign(offset, sqlvar->sqltype);
256 sqlvar->sqldata = (char*)__outBuffer + offset;
257 offset += rtypmsize(sqlvar->sqltype, sqlvar->sqllen);
258
259 if (!__fields[i].init(this, sqlvar))
260 return false;
261 }
262
263 return true;
264}
265
266bool IFXQuery::initParams(size_t _paramCount)
267{
268 __DCL_ASSERT((__params == NULL)
269 && (Query::__paramCount == 0)
270 && (__inSQLDA != NULL));
271#if 0
272 size_t nAllocSize = sizeof(ifx_sqlda_t) + (sizeof(ifx_sqlvar_t) * _paramCount);
273 __inSQLDA = (ifx_sqlda_t*)malloc(nAllocSize);
274 if (__inSQLDA == NULL) {
275 __SET_ERROR(SQL::eOutOfMemory);
276 return false;
277 }
278 memset(__inSQLDA, 0, nAllocSize);
279 __inSQLDA->sqld = _paramCount;
280 __inSQLDA->sqlvar = (ifx_sqlvar_t*)((char*)__inSQLDA + sizeof(ifx_sqlda_t));
281 __inSQLDA->desc_occ = sizeof(ifx_sqlda_t);
282#endif
283 Query::__paramCount = _paramCount;
284 __params = new IFXParam[Query::__paramCount];
285 if (__params == NULL) {
286 __SET_ERROR(SQL::eOutOfMemory);
287 return false;
288 }
289
290 ifx_sqlvar_t* sqlvar = __inSQLDA->sqlvar;
291 for(size_t i = 0; i < Query::__paramCount; i++, sqlvar++) {
292 if (!__params[i].init(this, sqlvar))
293 return false;
294 }
295
296 return true;
297}
298
299bool IFXQuery::__prepare(const char* _sql, size_t _sqllen,
300 size_t _paramCount)
301{
302 if (!reset())
303 return false;
304
305 EXEC SQL BEGIN DECLARE SECTION;
306 char* pszConnectionID = conn()->connectionID();
307 char* pszStatementID = (_CONST char*)__statementID.data();
308 char* pszSQL = (_CONST char*)_sql;
309 EXEC SQL END DECLARE SECTION;
310
311 EXEC SQL SET CONNECTION : pszConnectionID;
312 if (SQLCODE < 0) {
313 __SET_ERROR_SQLCODE(SQLCODE);
314 return false;
315 }
316 __DCL_ASSERT(SQLCODE == 0);
317
318 EXEC SQL PREPARE : pszStatementID FROM : pszSQL;
319 if (SQLCODE < 0) {
320 __SET_ERROR_SQLCODE(SQLCODE);
321 return false;
322 }
323
324 EXEC SQL DESCRIBE INPUT : pszStatementID INTO __inSQLDA;
325 if (SQLCODE < 0) {
326 __SET_ERROR_SQLCODE(SQLCODE);
327 return false;
328 }
329#if __TRACE_THIS && 1
330 if (__inSQLDA) {
331 __DCL_TRACE2_N(L"__inSQLDA[%p] sqld[%d]]\n",
332 __inSQLDA, __inSQLDA->sqld);
333 ifx_sqlvar_t* sqlvar = __inSQLDA->sqlvar;
334 for (int2 i = 0; i < __inSQLDA->sqld; i++, sqlvar++) {
335 __DCL_TRACE3_N(L"[%10hs][%2d] sqllen[%4d]\n",
336 rtypname(sqlvar->sqltype), sqlvar->sqltype, sqlvar->sqllen);
337 }
338 }
339#endif
340
341 EXEC SQL DESCRIBE OUTPUT :pszStatementID INTO __outSQLDA;
342 if (SQLCODE < 0) {
343 __SET_ERROR_SQLCODE(SQLCODE);
344 return false;
345 }
346
347 __DCL_TRACE1_N(L"stmtType[%ls]\n",
348 __STMT_STRING(SQLCODE == 0 ? SQ_SELECT : SQLCODE));
349#if __USE_STMT_TYPE
350 __stmtType = sqlca.sqlcode;
351 if (__stmtType == 0)
352 __stmtType = SQ_SELECT;
353
354 switch (__stmtType) {
355 case SQ_SELECT:
356 case SQ_EXECPROC: {
357 break;
358 }
359 default: {
360 if (__outSQLDA) {
361 ndebug_free(__outSQLDA);
362 __outSQLDA = NULL;
363 }
364 }
365 }
366#endif
367
368 if (__outSQLDA) {
369 __DCL_TRACE2_N(L"__outSQLDA[%p] sqld[%d]\n", __outSQLDA, __outSQLDA->sqld);
370 if (__outSQLDA->sqld > 0) {
371 if (!initFields())
372 return false;
373 }
374 }
375
376 if (_paramCount > 0) {
377 if (!initParams(_paramCount))
378 return false;
379 }
380
381 return true;
382}
383
384bool IFXQuery::__execute()
385{
386 EXEC SQL BEGIN DECLARE SECTION;
387 char* pszConnectionID = conn()->connectionID();
388 char* pszStatementID = (_CONST char*)__statementID.data();
389 char* pszCursorID = (_CONST char*)__cursorID.data();
390 EXEC SQL END DECLARE SECTION;
391
392 EXEC SQL SET CONNECTION :pszConnectionID;
393 if (SQLCODE < 0) {
394 __SET_ERROR_SQLCODE(SQLCODE);
395 return false;
396 }
397 __DCL_ASSERT(SQLCODE == 0);
398
399 //if (__stmtType == SQ_SELECT) {
400 if (__outSQLDA && __outSQLDA->sqld > 0) {
401 if (!__cursorDeclared) {
402 EXEC SQL DECLARE :pszCursorID CURSOR FOR :pszStatementID;
403 if (SQLCODE < 0) {
404 __SET_ERROR_SQLCODE(SQLCODE);
405 return false;
406 }
407 __cursorDeclared = true;
408 }
409
410 if (__cursorOpened) {
411 EXEC SQL CLOSE :pszCursorID;
412 if (SQLCODE < 0) {
413 __SET_ERROR_SQLCODE(SQLCODE);
414 return false;
415 }
416 __cursorOpened = false;
417 }
418
419 if (__inSQLDA)
420 EXEC SQL OPEN :pszCursorID USING DESCRIPTOR __inSQLDA;
421 else
422 EXEC SQL OPEN :pszCursorID;
423
424 if (SQLCODE < 0) {
425 __SET_ERROR_SQLCODE(SQLCODE);
426 return false;
427 }
428
429 __cursorOpened = true;
430 __eof = false;
431 }
432 else {
433#if __USE_STMT_TYPE
434 // 2025.05.06 __outSQLDA 조건은 CURSOR로 처리 하므로
435 // 다음 EXEC SQL EXECUTE는 의미가 없다.
436 if (__outSQLDA && __inSQLDA)
437 EXEC SQL EXECUTE :pszStatementID INTO DESCRIPTOR __outSQLDA
438 USING DESCRIPTOR __inSQLDA;
439 else if (__outSQLDA)
440 EXEC SQL EXECUTE :pszStatementID INTO DESCRIPTOR __outSQLDA;
441 else
442#endif
443 if (__inSQLDA)
444 EXEC SQL EXECUTE : pszStatementID USING DESCRIPTOR __inSQLDA;
445 else
446 EXEC SQL EXECUTE :pszStatementID;
447
448 if (SQLCODE < 0) {
449 __SET_ERROR_SQLCODE(SQLCODE);
450 Query::__affectedRows = -1;
451 return false;
452 }
453
454#if __USE_STMT_TYPE
455 if (__stmtType == SQ_EXECPROC) {
456 for(size_t i = 0; i < Query::__fieldCount; i++) {
457 if (!__fields[i].onAfterFetch())
458 return false;
459 }
460 }
461 else {
462 // INSERT, UPDATE, DELETE
463 Query::__affectedRows = sqlca.sqlerrd[2];
464 }
465#else
466 // INSERT, UPDATE, DELETE
467 Query::__affectedRows = sqlca.sqlerrd[2];
468#endif
469 }
470
471 for(size_t i = 0; i < Query::__paramCount; i++) {
472 if (!(__params[i].onAfterExecute()))
473 return false;
474 }
475
476 return true;
477}
478
479bool IFXQuery::__fetch()
480{
481 // SELECT, EXECUTE PROCEDURE
482 __DCL_ASSERT(!eof());
483 __DCL_ASSERT(__outSQLDA && __outSQLDA->sqld > 0);
484
485 EXEC SQL BEGIN DECLARE SECTION;
486 char* pszCursorID = (_CONST char*)__cursorID.data();
487 EXEC SQL END DECLARE SECTION;
488
489 EXEC SQL FETCH :pszCursorID USING DESCRIPTOR __outSQLDA;
490
491 if (SQLCODE == 0) {
492 for (size_t i = 0; i < Query::__fieldCount; i++) {
493 if (!__fields[i].onAfterFetch())
494 return false;
495 }
496 return true;
497 }
498 else if (SQLCODE == SQLNOTFOUND) {
499 Query::__eof = true;
500 return true;
501 }
502
503 __SET_ERROR_SQLCODE(SQLCODE);
504 return false;
505}
506
507bool IFXQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
508{
509 __DCL_ASSERT(Query::__fieldCount > 0);
510 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
511 *_fieldHandleOut = &__fields[_index];
512 return true;
513}
514
515bool IFXQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
516{
517 __DCL_ASSERT(Query::__paramCount > 0);
518 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
519 *_paramHandleOut = &__params[_index];
520 return true;
521}
522
523__DCL_END_NAMESPACE