DCL 3.7.4
Loading...
Searching...
No Matches
IBQuery.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <stdlib.h> // malloc, free
4
5#include <ibase.h>
6
7#include <dcl/Object.h>
8#if __DCL_HAVE_ALLOC_DEBUG
9#undef __DCL_ALLOC_LEVEL
10#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
11#endif
12
13#include <dcl/SQLCore.h>
14#include <dcl/OutputStream.h>
15#include <dcl/Charset.h>
16
17#include "IBConnection.h"
18#include "IBQuery.h"
19#include "IBField.h"
20#include "IBParam.h"
21
22#ifndef SQLDA_CURRENT_VERSION
23 #define SQLDA_CURRENT_VERSION SQLDA_VERSION1
24#endif
25
26#define __TRACE_THIS 0
27#if __TRACE_THIS
28#define __DCL_TRACE0_N __DCL_TRACE0
29#define __DCL_TRACE1_N __DCL_TRACE1
30#define __DCL_TRACE2_N __DCL_TRACE2
31#define __DCL_TRACE3_N __DCL_TRACE3
32#define __DCL_TRACE4_N __DCL_TRACE4
33#else
34#define __DCL_TRACE0_N(fmt)
35#define __DCL_TRACE1_N(fmt, arg)
36#define __DCL_TRACE2_N(fmt, arg1, arg2)
37#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
38#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
39#endif
40
41#undef __THIS_FILE__
42static const char_t __THIS_FILE__[] = __T("dcl/sql/IBQuery.cpp");
43
44__DCL_BEGIN_NAMESPACE
45
46#define __SET_ERROR(_error) \
47 conn()->setErrorHandle(_error, __THIS_FILE__, __LINE__)
48#define __SET_ERROR_MSG(_msg) \
49 conn()->setErrorMessage(_msg, __THIS_FILE__, __LINE__)
50
52
54 : Query(_connHandle)
55{
56 __stmtHandle = NULL_HANDLE;
57 __inSQLDA = NULL;
58 __outSQLDA = NULL;
59
60 __stmtType = _IB_STMT_TYPE_UNKNOWN;
61 __fields = NULL;
62 __params = NULL;
63}
64
66{
67#ifdef __DCL_DEBUG
68 if (!reset()) {
69 char buf[256];
70 size_t buflen = sizeof(buf) - 1;
71 bool b = conn()->__getErrorMessage(buf, &buflen);
72 buf[b ? buflen : 0] = '\0';
73 __DCL_TRACE1(L"Warning! Query reset error! %hs\n", buf);
74 }
75#else
76 (void)reset();
77#endif
78}
79
81{
82 delete this;
83}
84
86{
87 Query::__eof = true;
88 Query::__affectedRows = -1;
91
92 // clear fields
93 if (__fields) {
94 __DCL_ASSERT(Query::__fieldCount > 0);
95 delete[] __fields;
96 __fields = NULL;
97 Query::__fieldCount = 0;
98 }
99
100 // clear binds
101 if (__params) {
102 __DCL_ASSERT(Query::__paramCount > 0);
103 delete[] __params;
104 __params = NULL;
105 Query::__paramCount = 0;
106 }
107
108 if (__outSQLDA) {
109 free(__outSQLDA);
111 }
112
113 if (__inSQLDA) {
114 free(__inSQLDA);
115 __inSQLDA = NULL;
116 }
117
118 ISC_STATUS* statusVector = conn()->statusVector();
119 bool r = true;
120 if (__stmtHandle) {
121 if (isc_dsql_free_statement(
122 statusVector,
123 &__stmtHandle,
124 DSQL_drop
125 )) {
127 r = false;
128 }
130 }
131 return r;
132}
133
134static size_t __TYPE_ALIGN(size_t _offset, int _sqltype)
135{
136 size_t size = 0;
137 switch(_sqltype & ~1) {
138 case SQL_BOOLEAN:
139 size = sizeof(char);
140 break;
141 case SQL_SHORT:
142 size = sizeof(short);
143 break;
144 case SQL_LONG:
145 size = sizeof(long);
146 break;
147 case SQL_INT64:
148 size = sizeof(ISC_INT64);
149 break;
150 case SQL_FLOAT:
151 size = sizeof(float);
152 break;
153 case SQL_DOUBLE:
154 size = sizeof(double);
155 break;
156 case SQL_TYPE_DATE:
157 size = sizeof(ISC_DATE);
158 break;
159 case SQL_TYPE_TIME:
160 size = sizeof(ISC_TIME);
161 break;
162 case SQL_TIMESTAMP:
163 size = sizeof(ISC_TIMESTAMP);
164 break;
165#if defined(FIREBIRD_IBASE_H) && FB_API_VER >= 40
166 case SQL_TIME_TZ_EX:
167 size = sizeof(ISC_TIME_TZ_EX);
168 break;
169 case SQL_TIMESTAMP_TZ_EX:
170 size = sizeof(ISC_TIMESTAMP_TZ_EX);
171 break;
172#endif
173 case SQL_TEXT:
174 size = sizeof(char);
175 break;
176 case SQL_VARYING:
177 size = sizeof(short);
178 break;
179 case SQL_BLOB :
180 size = sizeof(ISC_QUAD);
181 break;
182 default: {
183 __DCL_TRACE3_N(__T("_offset[%d] _sqltype[%d] [%d]\n"),
184 _offset, _sqltype, _sqltype & ~1);
185 __DCL_ASSERT(false);
186 }
187 }
188
189 return (_offset + size - 1) & ~(size - 1);
190}
191
193{
195 && (Query::__fieldCount == 0)
196 && (__fields == NULL)
197 && (__outSQLDA == NULL)
198 );
199
200 ISC_STATUS* statusVector = conn()->statusVector();
201 __outSQLDA = (XSQLDA*)malloc(XSQLDA_LENGTH(1));
202 if (__outSQLDA == NULL) {
204 return false;
205 }
207 __outSQLDA->sqln = 1;
208
209 if(isc_dsql_describe(
210 statusVector,
211 &__stmtHandle,
214 )) {
216 return false;
217 }
218
219 if (__outSQLDA->sqld == 0) {
220 // 필드가 없다.
221 return true;
222 }
223
224 if (__outSQLDA->sqln < __outSQLDA->sqld) {
225 __outSQLDA = (XSQLDA*)realloc(__outSQLDA, XSQLDA_LENGTH(__outSQLDA->sqld));
226 if (__outSQLDA == NULL) {
228 return false;
229 }
231 __outSQLDA->sqln = __outSQLDA->sqld;
232
233 if (isc_dsql_describe(
234 statusVector,
235 &__stmtHandle,
238 )) {
240 return false;
241 }
242 }
243
244 // __outSQLDA에 적용할, 레코드 버퍼 크기를 계산한다.
245 size_t offset = 0;
246 XSQLVAR* sqlvar = __outSQLDA->sqlvar;
247 for(short i = 0; i < __outSQLDA->sqld; i++, sqlvar++) {
248 offset = __TYPE_ALIGN(offset, sqlvar->sqltype);
249 switch(sqlvar->sqltype & ~1) {
250 case SQL_TEXT :
251 offset += sqlvar->sqllen + 1;
252 break;
253 case SQL_VARYING :
254 offset += sqlvar->sqllen + sizeof(short) + 1;
255 break;
256 default :
257 offset += sqlvar->sqllen;
258 }
259 }
260
261 __DCL_TRACE1_N(L"outBuffer [%zd]\n", offset);
262 if (offset > 0) {
263 __outSQLDA = (XSQLDA*)realloc(__outSQLDA,
264 XSQLDA_LENGTH(__outSQLDA->sqld) + offset);
265 if (__outSQLDA == NULL) {
267 return false;
268 }
269 }
270
271 char* outBuffer = (char*)__outSQLDA + XSQLDA_LENGTH(__outSQLDA->sqld);
272 __DCL_TRACE2_N(L"__outSQLDA[%p] outBuffer[%p]\n", __outSQLDA, outBuffer);
273 sqlvar = __outSQLDA->sqlvar;
274 offset = 0;
275 for(short i = 0; i < __outSQLDA->sqld; i++, sqlvar++) {
276 offset = __TYPE_ALIGN(offset, sqlvar->sqltype);
277 sqlvar->sqldata = outBuffer + offset;
278 switch(sqlvar->sqltype & ~1) {
279 case SQL_TEXT :
280 offset += sqlvar->sqllen + 1;
281 break;
282 case SQL_VARYING :
283 offset += sqlvar->sqllen + sizeof(short) + 1;
284 break;
285 default :
286 offset += sqlvar->sqllen;
287 }
288 }
289
290 Query::__fieldCount = __outSQLDA->sqld;
291 __fields = new IBField[Query::__fieldCount];
292 if (__fields == NULL) {
294 return false;
295 }
296
297 sqlvar = __outSQLDA->sqlvar;
298 for (size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
299 if (!__fields[i].init(this, sqlvar))
300 return false;
301 }
302
303 return true;
304}
305
306bool IBQuery::initParams(size_t _paramCount)
307{
309 && (Query::__paramCount == 0)
310 && (__params == NULL)
311 && (__inSQLDA == NULL)
312 );
313
314 short paramCount = (short)_paramCount;
315 ISC_STATUS* statusVector = conn()->statusVector();
316 __inSQLDA = (XSQLDA*)malloc(XSQLDA_LENGTH(paramCount));
317 if (__inSQLDA == NULL) {
319 return false;
320 }
322 __inSQLDA->sqln = paramCount;
323
324 if (isc_dsql_describe_bind(
325 statusVector,
326 &__stmtHandle,
329 )) {
331 return false;
332 }
333 __DCL_ASSERT(__inSQLDA->sqld == paramCount);
334
335 Query::__paramCount = __inSQLDA->sqld;
336 __params = new IBParam[Query::__paramCount];
337 if (__params == NULL) {
339 return false;
340 }
341
342 XSQLVAR* sqlvar = __inSQLDA->sqlvar;
343 for(size_t i = 0; i < Query::__paramCount; i++, sqlvar++) {
344 if (!__params[i].init(this, sqlvar)) {
345 return false;
346 }
347 }
348
349 return true;
350}
351
352static char stmt_info_item[] = { isc_info_sql_stmt_type };
353static char count_info_item[] = { isc_info_sql_records };
354
355bool IBQuery::__prepare(const char* _sql, size_t _sqllen,
356 size_t _paramCount)
357{
358 if(!reset())
359 return false;
360
361 if (isc_dsql_allocate_statement(
362 conn()->statusVector(),
363 conn()->dbHandlePtr(),
365 )) {
367 return false;
368 }
369
370#ifdef FIREBIRD_IBASE_H
371 #undef _CONST
372 #define _CONST const
373#endif
374
375 ISC_STATUS rs = isc_dsql_prepare(
376 conn()->statusVector(),
377 conn()->trHandlePtr(),
378 &__stmtHandle,
379 0,
380 (_CONST char*)_sql,
381 conn()->dialect(),
382 NULL
383 );
384 if (rs) {
386 return false;
387 }
388
389 char res_buffer[8];
390 if (isc_dsql_sql_info(
391 conn()->statusVector(),
393 sizeof(stmt_info_item),
394 stmt_info_item,
395 sizeof(res_buffer),
396 res_buffer
397 )) {
399 return false;
400 }
401
402 if (res_buffer[0] != isc_info_sql_stmt_type) {
404 return false;
405 }
406
407 int len = isc_vax_integer(&res_buffer[1], 2);
408 __stmtType = isc_vax_integer(&res_buffer[3], len);
409
410 // SELECT, EXECUTE PROCEDURE 는 Row Sets을 반환한다.
411 if ((__stmtType == isc_info_sql_stmt_select)
412 || (__stmtType == isc_info_sql_stmt_select_for_upd)
413 || (__stmtType == isc_info_sql_stmt_exec_procedure)
414 ) {
415 if (!initFields())
416 return false;
417 }
418
419 if (_paramCount > 0) {
420 if (!initParams(_paramCount))
421 return false;
422 }
423
424 return true;
425// rs = isc_dsql_set_cursor_name(statusVector, &__stmtHandle, "CURSOR1", 0);
426}
427
429{
430 if (__stmtType == isc_info_sql_stmt_exec_procedure) {
431 // 2025-07-10
432 // EXECUTE PROCEDURE에 대하여 커서를 만들지 않는다.
433 // 따라서, __fetch()에서 isc_dsql_fetch는 사용할 수 없다.
434 if (isc_dsql_execute2(
435 conn()->statusVector(),
436 conn()->trHandlePtr(),
438 conn()->dialect(),
439 __inSQLDA,
441 )) {
443 return false;
444 }
445 }
446 else {
447 if (isc_dsql_execute(
448 conn()->statusVector(),
449 conn()->trHandlePtr(),
451 conn()->dialect(),
453 )) {
455 return false;
456 }
457 }
458
459 for (size_t i = 0; i < Query::__paramCount; i++) {
460 __params[i].onAfterExecute();
461 }
462
463 // INSERT, DELETE, UPDATE statement의 실행 후
464 // affected records를 구한다.
465
466 unsigned char count_type = 0;
467 switch(__stmtType) {
468 case isc_info_sql_stmt_update: {
469 count_type = isc_info_req_update_count;
470 break;
471 }
472 case isc_info_sql_stmt_delete: {
473 count_type = isc_info_req_delete_count;
474 break;
475 }
476 case isc_info_sql_stmt_insert: {
477 count_type = isc_info_req_insert_count;
478 break;
479 }
480 case isc_info_sql_stmt_select:
481 case isc_info_sql_stmt_select_for_upd: {
482 Query::__eof = false;
483 break;
484 }
485 case isc_info_sql_stmt_exec_procedure: {
486 Query::__eof = false;
487 for (size_t i = 0; i < Query::__fieldCount; i++) {
488 if (!__fields[i].onAfterFetch())
489 return false;
490 }
491 break;
492 }
493 }
494
495 if (count_type) {
496 unsigned char count_is = 0;
497 char count_buffer[33];
498 unsigned short length;
499
500 if (isc_dsql_sql_info(
501 conn()->statusVector(),
502 &__stmtHandle,
503 sizeof(count_info_item),
504 count_info_item,
505 sizeof(count_buffer),
506 count_buffer
507 )) {
509 return false;
510 }
511
512 for(char* p = count_buffer + 3; *p != isc_info_end; ) {
513 count_is = *p++;
514 length = (unsigned short)isc_vax_integer(p, 2);
515 p += 2;
516 Query::__affectedRows = isc_vax_integer(p, length);
517 p += length;
518 if (count_is == count_type)
519 break;
520 }
521 }
522
523 return true;
524}
525
527{
528 __DCL_ASSERT(!eof());
529 if (__stmtType == isc_info_sql_stmt_exec_procedure) {
530 // 2025-07-10
531 // SQLDA 구조를 따르는 Informix, PostgreSQL과 MariaDB 모두
532 // PROCEDURE 실행의 결과를 필드 접근으로 구현되었다.
533 // fetch 후 필드 접근 패턴으로 맞춘다.
535 if (__fetchCounter > 1) {
536 Query::__eof = true;
537 }
538 return true;
539 }
540
541 __DCL_ASSERT(__stmtType == isc_info_sql_stmt_select
542 || __stmtType == isc_info_sql_stmt_select_for_upd);
543 ISC_STATUS rs = isc_dsql_fetch(
544 conn()->statusVector(),
545 &__stmtHandle,
548 );
549 if (rs == 0) {
550 for(size_t i = 0; i < Query::__fieldCount; i++) {
551 if (!__fields[i].onAfterFetch())
552 return false;
553 }
554 return true;
555 }
556 else if (rs == 100) {
557 Query::__eof = true;
558 return true;
559 }
560
561 __DCL_TRACE1(L"[%zd]\n", (int64_t)rs);
563 return false;
564}
565
566bool IBQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
567{
568 __DCL_ASSERT(Query::__fieldCount > 0);
569 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
570 *_fieldHandleOut = &__fields[_index];
571 return true;
572}
573
574bool IBQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
575{
576 __DCL_ASSERT(Query::__paramCount > 0);
577 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
578 *_paramHandleOut = &__params[_index];
579 return true;
580}
581
582__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:312
wchar_t char_t
Definition Config.h:247
#define _CONST
Definition Config.h:325
#define __DCL_TRACE1_N(fmt, arg)
#define __DCL_TRACE2_N(fmt, arg1, arg2)
#define NULL_HANDLE
#define SQLDA_CURRENT_VERSION
Definition IBQuery.cpp:23
#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
Definition IBQuery.cpp:37
#define _IB_STMT_TYPE_UNKNOWN
Definition IBQuery.h:6
IOException *size_t r
Definition MediaInfo.cpp:82
#define __DCL_TRACE1(fmt, arg1)
Definition Object.h:399
#define __DCL_ASSERT(expr)
Definition Object.h:394
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:245
#define __T(str)
Definition Object.h:60
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:149
IBQuery(IBConnection *_connHandle)
IBParam * __params
Definition IBQuery.h:25
bool initParams(size_t _paramCount)
Definition IBQuery.cpp:306
virtual void __destroy()
Definition IBQuery.cpp:80
virtual bool __fetch()
Definition IBQuery.cpp:526
virtual bool __getField(size_t _index, SQL::Field **_fieldHandleOut)
Definition IBQuery.cpp:566
virtual bool __prepare(const char *_sql, size_t _sqllen, size_t _paramCount)
Definition IBQuery.cpp:355
int __stmtType
Definition IBQuery.h:21
IBField * __fields
Definition IBQuery.h:26
bool initFields()
Definition IBQuery.cpp:192
XSQLDA * __inSQLDA
Definition IBQuery.h:22
virtual ~IBQuery()
Definition IBQuery.cpp:65
bool reset()
Definition IBQuery.cpp:85
XSQLDA * __outSQLDA
Definition IBQuery.h:23
virtual bool __getParam(size_t _index, SQL::Param **_paramHandleOut)
Definition IBQuery.cpp:574
virtual bool __execute()
Definition IBQuery.cpp:428
int __fetchCounter
Definition IBQuery.h:29
isc_stmt_handle __stmtHandle
Definition IBQuery.h:20
@ eOutOfMemory
Definition SQLCore.h:24
@ eServerError
Definition SQLCore.h:21