7#include <stdlib.h> // malloc, free
8#include <string.h> // memset
10#include <dcl/Object.h>
11#if __DCL_HAVE_ALLOC_DEBUG
12#undef __DCL_ALLOC_LEVEL
13#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
16#include <dcl/Numeric.h>
17#include <dcl/SQLCore.h>
19#include "IFXConnection.h"
23#include "IFXUtils.h" // for void ndebug_free(void* _pv)
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
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)
41static const char_t __THIS_FILE__[] = __T("dcl/sql/IFXQuery.ec");
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__)
52IMPLEMENT_CLASSINFO(IFXQuery, SQL::Query)
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)
63 __SQ_NAME(SQ_EXECPROC)
69IFXQuery::IFXQuery(IFXConnection* _connection)
73 strID = ByteString::format("%zx", (size_t)this);
74 __statementID = "stmt_" + strID;
75 __cursorID = "curs_" + strID;
78 __stmtType = __SQ_UNKNOWN;
84 __cursorOpened = false;
85 __cursorDeclared = false;
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);
106void IFXQuery::__destroy()
108// cerr << "IFXQuery::destory\n";
112bool IFXQuery::reset()
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;
122 EXEC SQL SET CONNECTION :connID;
123 if (r && SQLCODE < 0) {
124 __SET_ERROR_HANDLE(SQLCODE);
127 __DCL_ASSERT(SQLCODE == 0);
129 if (__cursorOpened) {
130 __cursorOpened = false;
131 EXEC SQL CLOSE :cursorID;
132 if (r && SQLCODE < 0) {
133 __SET_ERROR_HANDLE(SQLCODE);
138 if (__cursorDeclared) {
139 __cursorDeclared = false;
140 EXEC SQL FREE :cursorID;
141 if (r && SQLCODE < 0) {
142 __SET_ERROR_HANDLE(SQLCODE);
148 if (__stmtType != __SQ_UNKNOWN) {
149 EXEC SQL FREE :stmtID;
150 __stmtType = __SQ_UNKNOWN;
153 if (__outSQLDA || __inSQLDA) {
154 EXEC SQL FREE :stmtID;
155 if (r && SQLCODE < 0) {
156 __SET_ERROR_HANDLE(SQLCODE);
163 Query::__affectedRows = -1;
167 __DCL_ASSERT(Query::__fieldCount > 0);
170 Query::__fieldCount = 0;
175 __DCL_ASSERT(Query::__paramCount > 0);
178 Query::__paramCount = 0;
187 // __outSQLDA는 Informix API에서 할당됨.
188 ndebug_free(__outSQLDA);
193 ndebug_free(__inSQLDA);
200bool IFXQuery::initFields()
203 (Query::__fieldCount == 0)
204 && (__fields == NULL)
205 && (__outBuffer == NULL)
206 && (__outSQLDA != NULL)
207 && (__outSQLDA->sqld > 0)
210 // __outSQLDA에 적용할, 레코드 버퍼 크기를 계산한다.
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,
220 switch (sqlvar->sqltype & SQLTYPE) {
227 sqlvar->sqllen = msize;
231 SQLTYPE sqllen rtypmsize
232 ===========================================
233 SQLCHAR 서버컬럼최대길이 sqllen + 1
234 SQLVCHAR '\0'을 포함한 버퍼의 크기
238 -------------------------------------------
239 SQLDECIMAL DECIMAL(p,s) sizeof(dec_t)
241 -------------------------------------------
242 SQLDTIME qualifier sizeof(dtime_t)
243 SQLINTERVAL qualifier sizeof(intrvl_t)
247 __DCL_TRACE1_N(L"outBuffer [%zd]\n", offset);
249 __outBuffer = (char*)malloc(offset);
250 __DCL_ASSERT((size_t)__outBuffer % sizeof(void*) == 0);
251 if (__outBuffer == NULL) {
252 __SET_ERROR(SQL::eOutOfMemory);
257 sqlvar = __outSQLDA->sqlvar;
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);
265 Query::__fieldCount = __outSQLDA->sqld;
266 __fields = new IFXField[Query::__fieldCount];
267 if (__fields == NULL) {
268 __SET_ERROR(SQL::eOutOfMemory);
272 sqlvar = __outSQLDA->sqlvar;
273 for (size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
274 if (!__fields[i].init(this, sqlvar))
281bool IFXQuery::initParams(size_t _paramCount)
283 __DCL_ASSERT((Query::__paramCount == 0)
284 && (__params == NULL)
285 && (__inSQLDA != NULL)
286 && (__inSQLDA->sqld > 0)
289 __DCL_ASSERT(__inSQLDA->sqld == (int2)_paramCount);
291 Query::__paramCount = __inSQLDA->sqld;
292 __params = new IFXParam[Query::__paramCount];
293 if (__params == NULL) {
294 __SET_ERROR(SQL::eOutOfMemory);
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))
307bool IFXQuery::__prepare(const char* _sql, size_t _sqllen,
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;
319 EXEC SQL SET CONNECTION : connID;
321 __SET_ERROR_HANDLE(SQLCODE);
324 __DCL_ASSERT(SQLCODE == 0);
326 EXEC SQL PREPARE : stmtID FROM : sql;
328 __SET_ERROR_HANDLE(SQLCODE);
332 EXEC SQL DESCRIBE INPUT : stmtID INTO __inSQLDA;
334 __SET_ERROR_HANDLE(SQLCODE);
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);
349 EXEC SQL DESCRIBE OUTPUT :stmtID INTO __outSQLDA;
351 __SET_ERROR_HANDLE(SQLCODE);
355 __DCL_TRACE1_N(L"stmtType[%ls]\n",
356 __STMT_STRING(SQLCODE == 0 ? SQ_SELECT : SQLCODE));
358 __stmtType = sqlca.sqlcode;
360 __stmtType = SQ_SELECT;
362 switch (__stmtType) {
369 ndebug_free(__outSQLDA);
376 __DCL_TRACE2_N(L"__outSQLDA[%p] sqld[%d]\n",
377 __outSQLDA, __outSQLDA ? __outSQLDA->sqld : -1);
378 if (__outSQLDA && __outSQLDA->sqld > 0) {
383 __DCL_TRACE2_N(L"__inSQLDA[%p] sqld[%d]\n",
384 __inSQLDA, __inSQLDA ? __inSQLDA->sqld : -1);
385 if (_paramCount > 0) {
386 if (!initParams(_paramCount))
393bool IFXQuery::__execute()
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;
401 EXEC SQL SET CONNECTION :connID;
403 __SET_ERROR_HANDLE(SQLCODE);
406 __DCL_ASSERT(SQLCODE == 0);
408 //if (__stmtType == SQ_SELECT) {
409 if (__outSQLDA && __outSQLDA->sqld > 0) {
410 if (!__cursorDeclared) {
411 EXEC SQL DECLARE :cursorID CURSOR FOR :stmtID;
413 __SET_ERROR_HANDLE(SQLCODE);
416 __cursorDeclared = true;
419 if (__cursorOpened) {
420 EXEC SQL CLOSE :cursorID;
422 __SET_ERROR_HANDLE(SQLCODE);
425 __cursorOpened = false;
429 EXEC SQL OPEN :cursorID USING DESCRIPTOR __inSQLDA;
431 EXEC SQL OPEN :cursorID;
434 __SET_ERROR_HANDLE(SQLCODE);
438 __cursorOpened = true;
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;
449 EXEC SQL EXECUTE :stmtID INTO DESCRIPTOR __outSQLDA;
453 EXEC SQL EXECUTE : stmtID USING DESCRIPTOR __inSQLDA;
455 EXEC SQL EXECUTE :stmtID;
458 __SET_ERROR_HANDLE(SQLCODE);
459 Query::__affectedRows = -1;
464 if (__stmtType == SQ_EXECPROC) {
465 for(size_t i = 0; i < Query::__fieldCount; i++) {
466 if (!__fields[i].onAfterFetch())
471 // INSERT, UPDATE, DELETE
472 Query::__affectedRows = sqlca.sqlerrd[2];
475 // INSERT, UPDATE, DELETE
476 Query::__affectedRows = sqlca.sqlerrd[2];
480 for(size_t i = 0; i < Query::__paramCount; i++) {
481 if (!(__params[i].onAfterExecute()))
488bool IFXQuery::__fetch()
490 // SELECT, EXECUTE PROCEDURE
491 __DCL_ASSERT(!eof());
492 __DCL_ASSERT(__outSQLDA && __outSQLDA->sqld > 0);
494 EXEC SQL BEGIN DECLARE SECTION;
495 char* connID = conn()->connectionID();
496 char* cursorID = (_CONST char*)__cursorID.data();
497 EXEC SQL END DECLARE SECTION;
499 EXEC SQL SET CONNECTION :connID;
501 __SET_ERROR_HANDLE(SQLCODE);
504 __DCL_ASSERT(SQLCODE == 0);
506 EXEC SQL FETCH :cursorID USING DESCRIPTOR __outSQLDA;
509 for (size_t i = 0; i < Query::__fieldCount; i++) {
510 if (!__fields[i].onAfterFetch())
515 else if (SQLCODE == SQLNOTFOUND) {
517 if (__cursorOpened) {
518 __cursorOpened = false;
519 EXEC SQL CLOSE :cursorID;
521 __SET_ERROR_HANDLE(SQLCODE);
528 __SET_ERROR_HANDLE(SQLCODE);
532bool IFXQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
534 __DCL_ASSERT(Query::__fieldCount > 0);
535 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
536 *_fieldHandleOut = &__fields[_index];
540bool IFXQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
542 __DCL_ASSERT(Query::__paramCount > 0);
543 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
544 *_paramHandleOut = &__params[_index];