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