DCL 4.1
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#undef __THIS_FILE__
27static const wchar_t __THIS_FILE__[] = __T("dcl/sql/IBQuery.cpp");
28
29__DCL_BEGIN_NAMESPACE
30
32
34 : Query(_connHandle)
35{
36 __stmtHandle = NULL;
37 __inSQLDA = NULL;
38 __outSQLDA = NULL;
39 __outBuffer = NULL;
40
41 __stmtType = _IB_STMT_TYPE_UNKNOWN; // unknown
42 __fields = NULL;
43 __params = NULL;
44}
45
47{
48#ifdef __DCL_DEBUG
49 if (!reset()) {
50 ByteString s;
51 size_t n = 512;
52 ByteBuffer* buf = ByteBuffer::create(n);
53 bool b = conn()->__getErrorMessage(buf->data(), &n);
54 if (b) {
55 buf->__dataLength = n;
56 s = buf;
57 }
58 buf->release();
59
60 if (b) {
61 __DCL_TRACE1(__T("Warning! %s\n"), s.data());
62 }
63 else {
64 __DCL_TRACE0(__T("Warning! Query reset error\n"));
65 }
66 }
67#else
68 (void)reset();
69#endif
70}
71
73{
74 delete this;
75}
76
78{
79 Query::__eof = true;
80 Query::__affectedRows = -1;
83
84 // clear fields
85 if (__fields) {
86 __DCL_ASSERT(Query::__fieldCount > 0);
87 delete[] __fields;
88 __fields = NULL;
89 Query::__fieldCount = 0;
90 }
91
92 // clear binds
93 if (__params) {
94 __DCL_ASSERT(Query::__paramCount > 0);
95 delete[] __params;
96 __params = NULL;
97 Query::__paramCount = 0;
98 }
99
100 if (__outBuffer) {
101 free(__outBuffer);
103 }
104
105 if (__outSQLDA) {
106 free(__outSQLDA);
108 }
109
110 if (__inSQLDA) {
111 free(__inSQLDA);
112 __inSQLDA = NULL;
113 }
114
115 ISC_STATUS* statusVector = conn()->statusVector();
116 bool r = true;
117 if (__stmtHandle) {
118 if (isc_dsql_free_statement(
119 statusVector,
120 &__stmtHandle,
121 DSQL_drop)
122 ) {
123// __DCL_ASSERT(STATUS_FAILED(statusVector));
125 r = false;
126 }
128 }
129 return r;
130}
131
132static size_t __TYPE_ALIGN(size_t _offset, int _sqltype)
133{
134 size_t size = 0;
135 switch(_sqltype & ~1) {
136 case SQL_SHORT :
137 size = sizeof(short);
138 break;
139 case SQL_LONG :
140 size = sizeof(long);
141 break;
142 case SQL_INT64 :
143 size = sizeof(ISC_INT64);
144 break;
145 case SQL_FLOAT :
146 size = sizeof(float);
147 break;
148 case SQL_DOUBLE :
149 size = sizeof(double);
150 break;
151 case SQL_TYPE_DATE :
152 size = sizeof(ISC_DATE);
153 break;
154 case SQL_TYPE_TIME :
155 size = sizeof(ISC_TIME);
156 break;
157 case SQL_TIMESTAMP :
158 size = sizeof(ISC_TIMESTAMP);
159 break;
160#if defined(FIREBIRD_IBASE_H) && FB_API_VER >= 40
161 case SQL_TIME_TZ_EX:
162 size = sizeof(ISC_TIME_TZ_EX);
163 break;
164 case SQL_TIMESTAMP_TZ_EX:
165 size = sizeof(ISC_TIMESTAMP_TZ_EX);
166 break;
167#endif
168 case SQL_TEXT :
169 size = sizeof(char);
170 break;
171 case SQL_VARYING :
172 size = sizeof(short);
173 break;
174 case SQL_BLOB :
175 size = sizeof(ISC_QUAD);
176 break;
177 default :
178 __DCL_TRACE3(__T("_offset[%d] _sqltype[%d] [%d]\n"), _offset, _sqltype, _sqltype & ~1);
179 __DCL_ASSERT(false);
180 }
181
182 return (_offset + size - 1) & ~(size - 1);
183}
184
186{
188 && (__outBuffer == NULL)
189 && (Query::__fieldCount == 0)
190 );
191
192 ISC_STATUS* statusVector = conn()->statusVector();
193
194 __outSQLDA = (XSQLDA*)malloc(XSQLDA_LENGTH(1));
196 __outSQLDA->sqln = 1;
197
198 if(isc_dsql_describe(
199 statusVector,
200 &__stmtHandle,
203 ) {
204// __DCL_ASSERT(STATUS_FAILED(statusVector));
206 return false;
207 }
208
209 if (__outSQLDA->sqld == 0) {
210 // 필드가 없다.
211 return true;
212 }
213
214 if (__outSQLDA->sqln < __outSQLDA->sqld) {
215 __outSQLDA = (XSQLDA*)realloc(__outSQLDA, XSQLDA_LENGTH(__outSQLDA->sqld));
217 __outSQLDA->sqln = __outSQLDA->sqld;
218
219 if (isc_dsql_describe(
220 statusVector,
221 &__stmtHandle,
224 ) {
225// __DCL_ASSERT(STATUS_FAILED(statusVector));
227 return false;
228 }
229 }
230
231 Query::__fieldCount = __outSQLDA->sqld;
232 __fields = new IBField[Query::__fieldCount];
233 if (__fields == NULL) {
235 return false;
236 }
237
238 size_t offset = 0;
239 XSQLVAR* sqlvar = __outSQLDA->sqlvar;
240 for(size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
241 offset = __TYPE_ALIGN(offset, sqlvar->sqltype);
242 switch(sqlvar->sqltype & ~1) {
243 case SQL_TEXT :
244 offset += sqlvar->sqllen + 1;
245 break;
246 case SQL_VARYING :
247 offset += sqlvar->sqllen + sizeof(short) + 1;
248 break;
249 default :
250 offset += sqlvar->sqllen;
251 }
252 }
253
254 if (offset > 0) {
255 __outBuffer = (char*)malloc(offset);
256 __DCL_ASSERT((size_t)__outBuffer % sizeof(void*) == 0);
257 if (!__outBuffer) {
259 return false;
260 }
261 }
262
263 sqlvar = __outSQLDA->sqlvar;
264 offset = 0;
265 for(size_t i = 0; i < Query::__fieldCount; i++, sqlvar++) {
266 offset = __TYPE_ALIGN(offset, sqlvar->sqltype);
267 sqlvar->sqldata = (char*)__outBuffer + offset;
268 switch(sqlvar->sqltype & ~1) {
269 case SQL_TEXT :
270 offset += sqlvar->sqllen + 1;
271 break;
272 case SQL_VARYING :
273 offset += sqlvar->sqllen + sizeof(short) + 1;
274 break;
275 default :
276 offset += sqlvar->sqllen;
277 }
278
279 if (!__fields[i].init(this, sqlvar))
280 return false;
281 }
282
283 return true;
284}
285
286bool IBQuery::initParams(size_t _paramCount)
287{
288 ISC_STATUS* statusVector = conn()->statusVector();
289
290 __inSQLDA = (XSQLDA*)malloc(XSQLDA_LENGTH(1));
292 __inSQLDA->sqln = 1;
293
294 if (isc_dsql_describe_bind(
295 statusVector,
296 &__stmtHandle,
298 __inSQLDA)
299 ) {
300// __DCL_ASSERT(STATUS_FAILED(statusVector));
302 return false;
303 }
304
305 if (__inSQLDA->sqld == 0) {
306 // 파라미터가 없다. bind 할 필요가 없다.
307 return true;
308 }
309
310 if (__inSQLDA->sqln < __inSQLDA->sqld) {
311 __inSQLDA = (XSQLDA*)realloc(__inSQLDA, XSQLDA_LENGTH(__inSQLDA->sqld));
313 __inSQLDA->sqln = __inSQLDA->sqld;
314
315 if (isc_dsql_describe_bind(
316 statusVector,
317 &__stmtHandle,
319 __inSQLDA)
320 ) {
321// __DCL_ASSERT(STATUS_FAILED(m_statusVector));
323 return false;
324 }
325 }
326 __DCL_ASSERT(__inSQLDA->sqld == _paramCount);
327
328 Query::__paramCount = _paramCount;
329 __params = new IBParam[Query::__paramCount];
330 if (__params == NULL) {
332 return false;
333 }
334
335 XSQLVAR* sqlvar = __inSQLDA->sqlvar;
336 for(size_t i = 0; i < Query::__paramCount; i++, sqlvar++) {
337 if (!__params[i].init(this, sqlvar)) {
338 return false;
339 }
340 }
341
342 return true;
343}
344
345static char stmt_info_item[] = { isc_info_sql_stmt_type };
346static char count_info_item[] = { isc_info_sql_records };
347
348bool IBQuery::__prepare(const char* _sql, size_t _sqllen,
349 size_t _paramCount)
350{
351 if(!reset())
352 return false;
353
354 IBConnection* connHandle = conn();
355 ISC_STATUS* statusVector = connHandle->statusVector();
356
357 if (isc_dsql_allocate_statement(
358 statusVector,
359 connHandle->dbHandlePtr(),
361 ) {
362// __DCL_ASSERT(STATUS_FAILED(m_statusVector));
364 return false;
365 }
366
367#ifdef FIREBIRD_IBASE_H
368 #undef _CONST
369 #define _CONST const
370#endif
371
372 ISC_STATUS rs = isc_dsql_prepare(
373 statusVector,
374 connHandle->trHandlePtr(),
375 &__stmtHandle,
376 0,
377 (_CONST char*)_sql,
378 connHandle->dialect(),
379 NULL
380 );
381 if (rs) {
382// __DCL_ASSERT(STATUS_FAILED(m_statusVector));
384 return false;
385 }
386
387 char res_buffer[8];
388 if (isc_dsql_sql_info(
389 statusVector,
391 sizeof(stmt_info_item),
392 stmt_info_item,
393 sizeof(res_buffer),
394 res_buffer)
395 ) {
396// __DCL_ASSERT(STATUS_FAILED(m_statusVector));
398 return false;
399 }
400
401 if (res_buffer[0] != isc_info_sql_stmt_type) {
403 return false;
404 }
405
406 int len = isc_vax_integer(&res_buffer[1], 2);
407 __stmtType = isc_vax_integer(&res_buffer[3], len);
408
409 // SELECT, EXECUTE PROCEDURE 는 Row Sets을 반환한다.
410 if ((__stmtType == isc_info_sql_stmt_select)
411 || (__stmtType == isc_info_sql_stmt_select_for_upd)
412 || (__stmtType == isc_info_sql_stmt_exec_procedure)) {
413 if (!initFields())
414 return false;
415 }
416
417 if (_paramCount > 0) {
418 if (!initParams(_paramCount))
419 return false;
420 }
421
422 return true;
423// rs = isc_dsql_set_cursor_name(statusVector, &__stmtHandle, "CURSOR1", 0);
424}
425
427{
428 IBConnection* connHandle = conn();
429 ISC_STATUS* statusVector = connHandle->statusVector();
430
431 if (__stmtType == isc_info_sql_stmt_exec_procedure) {
432 // 2025-07-10
433 // EXECUTE PROCEDURE에 대하여 커서를 만들지 않는다.
434 // 따라서, __fetch()에서 isc_dsql_fetch는 사용할 수 없다.
435 if (isc_dsql_execute2(
436 statusVector,
437 connHandle->trHandlePtr(),
439 connHandle->dialect(),
440 __inSQLDA,
441 __outSQLDA)) {
443 return false;
444 }
445 }
446 else {
447 if (isc_dsql_execute(
448 statusVector,
449 connHandle->trHandlePtr(),
451 connHandle->dialect(),
452 __inSQLDA)) {
453 // __DCL_ASSERT(STATUS_FAILED(statusVector));
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 statusVector,
502 &__stmtHandle,
503 sizeof(count_info_item),
504 count_info_item,
505 sizeof(count_buffer),
506 count_buffer)) {
507// __DCL_ASSERT(STATUS_FAILED(statusVector));
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(conn()->statusVector(),
544 &__stmtHandle,
546 __outSQLDA);
547 if (rs == 0) {
548 for(size_t i = 0; i < Query::__fieldCount; i++) {
549 if (!__fields[i].onAfterFetch())
550 return false;
551 }
552 return true;
553 }
554 else if (rs == 100) {
555 Query::__eof = true;
556 return true;
557 }
558
559 __DCL_TRACE1(L"[%zd]\n", (int64_t)rs);
561 return false;
562}
563
564bool IBQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
565{
566 __DCL_ASSERT(Query::__fieldCount > 0);
567 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
568 *_fieldHandleOut = &__fields[_index];
569 return true;
570}
571
572bool IBQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
573{
574 __DCL_ASSERT(Query::__paramCount > 0);
575 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
576 *_paramHandleOut = &__params[_index];
577 return true;
578}
579
580__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:340
#define _CONST
Definition Config.h:353
#define SQLDA_CURRENT_VERSION
Definition IBQuery.cpp:23
#define _IB_STMT_TYPE_UNKNOWN
Definition IBQuery.h:6
#define __DCL_TRACE0(psz)
Definition Object.h:375
#define __DCL_TRACE1(fmt, arg1)
Definition Object.h:376
#define __DCL_TRACE3(fmt, arg1, arg2, arg3)
Definition Object.h:378
#define __DCL_ASSERT(expr)
Definition Object.h:371
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:228
#define __T(str)
Definition Object.h:44
ByteString r
size_t len
ByteBuffer * buf
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:153
void CharsetConvertException *size_t n
Definition SQLField.cpp:254
short dialect() const
isc_tr_handle * trHandlePtr()
ISC_STATUS * statusVector()
isc_db_handle * dbHandlePtr()
IBQuery(IBConnection *_connHandle)
IBParam * __params
Definition IBQuery.h:30
bool initParams(size_t _paramCount)
Definition IBQuery.cpp:286
virtual void __destroy()
Definition IBQuery.cpp:72
virtual bool __fetch()
Definition IBQuery.cpp:526
char * __outBuffer
Definition IBQuery.h:28
virtual bool __getField(size_t _index, SQL::Field **_fieldHandleOut)
Definition IBQuery.cpp:564
virtual bool __prepare(const char *_sql, size_t _sqllen, size_t _paramCount)
Definition IBQuery.cpp:348
int __stmtType
Definition IBQuery.h:25
IBField * __fields
Definition IBQuery.h:31
bool initFields()
Definition IBQuery.cpp:185
XSQLDA * __inSQLDA
Definition IBQuery.h:26
virtual ~IBQuery()
Definition IBQuery.cpp:46
bool reset()
Definition IBQuery.cpp:77
XSQLDA * __outSQLDA
Definition IBQuery.h:27
virtual bool __getParam(size_t _index, SQL::Param **_paramHandleOut)
Definition IBQuery.cpp:572
virtual bool __execute()
Definition IBQuery.cpp:426
int __fetchCounter
Definition IBQuery.h:34
isc_stmt_handle __stmtHandle
Definition IBQuery.h:24
@ eOutOfMemory
Definition SQLCore.h:24
@ eServerError
Definition SQLCore.h:21