DCL 4.0
Loading...
Searching...
No Matches
MyQuery.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <stdlib.h> // malloc, free
4#include <string.h> // memset
5
6#include <dcl/Object.h>
7#if __DCL_HAVE_ALLOC_DEBUG
8#undef __DCL_ALLOC_LEVEL
9#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
10#endif
11
12#include <dcl/SQLCore.h>
13
14#include "MyConnection.h"
15#include "MyQuery.h"
16#include "MyField.h"
17#include "MyParam.h"
18
19#define __TRACE_THIS 0
20#if __TRACE_THIS
21#define __DCL_TRACE0_N __DCL_TRACE0
22#define __DCL_TRACE1_N __DCL_TRACE1
23#define __DCL_TRACE2_N __DCL_TRACE2
24#define __DCL_TRACE3_N __DCL_TRACE3
25#define __DCL_TRACE4_N __DCL_TRACE4
26#else
27#define __DCL_TRACE0_N(fmt)
28#define __DCL_TRACE1_N(fmt, arg)
29#define __DCL_TRACE2_N(fmt, arg1, arg2)
30#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
31#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
32#endif
33
34#if __DCL_HAVE_THIS_FILE__
35#undef __THIS_FILE__
36static const wchar_t __THIS_FILE__[] = __T("dcl/sql/MyQuery.cpp");
37#endif
38
39__DCL_BEGIN_NAMESPACE
40
42
44 : Query(pConnection)
45{
46 __stmt = NULL;
47 __inBINDs = NULL;
48 __outBINDs = NULL;
49 __outBuffer = NULL;
50
51 __fields = NULL;
52 __params = NULL;
53}
54
56{
57#ifdef __DCL_DEBUG
58 if (!reset()) {
59 ByteString s;
60 size_t n = 512;
61 ByteBuffer* buf = ByteBuffer::create(n);
62 bool b = conn()->__getErrorMessage(buf->data(), &n);
63 if (b) {
64 buf->__dataLength = n;
65 s = buf;
66 }
67 buf->release();
68
69 if (b) {
70 __DCL_TRACE1_N(__T("Warning! %s\n"), s.data());
71 }
72 else {
73 __DCL_TRACE0_N(__T("Warning! Query reset error\n"));
74 }
75 }
76#else
77 (void)reset();
78#endif
79}
80
82{
83 delete this;
84}
85
87{
88 Query::__eof = true;
89 Query::__affectedRows = -1;
90
91 if (__fields) {
92 __DCL_ASSERT(Query::__fieldCount > 0);
93 delete[] __fields;
94 __fields = NULL;
95 Query::__fieldCount = 0;
96 }
97
98 if (__params) {
99 __DCL_ASSERT(Query::__paramCount > 0);
100 delete[] __params;
101 __params = NULL;
102 Query::__paramCount = 0;
103 }
104
105 if (__outBuffer) {
106 free(__outBuffer);
108 }
109
110 if (__outBINDs) {
111 free(__outBINDs);
113 }
114
115 if (__inBINDs) {
116 free(__inBINDs);
117 __inBINDs = NULL;
118 }
119
120 bool r = true;
121 if (__stmt) {
122 __DCL_TRACE2_N(L"state[%d] result.data[%p]\n",
123 __stmt->state, __stmt->result.data);
124 if (__stmt->state >= MYSQL_STMT_USE_OR_STORE_CALLED) {
125 if (mysql_stmt_free_result(__stmt)) {
126 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
127 mysql_stmt_errno(__stmt),
128 mysql_stmt_error(__stmt)));
129 return false;
130 }
131 }
132 if (mysql_stmt_close(__stmt)) {
133 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
134 mysql_errno(conn()->connHandle()),
135 mysql_error(conn()->connHandle())));
136 r = false;
137 }
138 __stmt = NULL;
139 }
140
141 return r;
142}
143
144static unsigned int __TYPE_SIZE(const MYSQL_FIELD& _field)
145{
146 unsigned int size = 0;
147 switch (_field.type) {
148 case MYSQL_TYPE_TINY :
149 size = sizeof(int8_t);
150 break;
151 case MYSQL_TYPE_YEAR :
152 case MYSQL_TYPE_SHORT :
153 size = sizeof(int16_t);
154 break;
155 case MYSQL_TYPE_INT24 :
156 case MYSQL_TYPE_LONG :
157 size = sizeof(int32_t);
158 break;
159 case MYSQL_TYPE_LONGLONG :
160 size = sizeof(int64_t);
161 break;
162 case MYSQL_TYPE_FLOAT :
163 size = sizeof(float);
164 break;
165 case MYSQL_TYPE_DOUBLE :
166 size = sizeof(double);
167 break;
168 case MYSQL_TYPE_DATE:
169 case MYSQL_TYPE_TIME:
170 case MYSQL_TYPE_DATETIME:
171 case MYSQL_TYPE_TIMESTAMP:
172 size = sizeof(MYSQL_TIME);
173 break;
174 case MYSQL_TYPE_BIT:
175 size = sizeof(int64_t); // cf. _field.length == 64 64bits
176 break;
177 default:
178 // 1000문자(3 * 1000바이트)까지 버퍼를 미리 할당한다.
179 // 그 외에는 mysql_stmt_fetch_column()을 사용한다.
180 // UTF-8, TEXT의 경우 _field.length는 3 * 2^16를 지시한다.
181 size = _field.length <= 3000 ? _field.length : 0;
182 }
183 return size;
184}
185
186static size_t __TYPE_ALIGN(size_t _offset, const MYSQL_FIELD& _field)
187{
188 size_t size = 0;
189 switch (_field.type) {
190 case MYSQL_TYPE_TINY:
191 size = sizeof(int8_t);
192 break;
193 case MYSQL_TYPE_YEAR:
194 case MYSQL_TYPE_SHORT:
195 size = sizeof(int16_t);
196 break;
197 case MYSQL_TYPE_INT24:
198 case MYSQL_TYPE_LONG:
199 size = sizeof(int32_t);
200 break;
201 case MYSQL_TYPE_LONGLONG:
202 size = sizeof(int64_t);
203 break;
204 case MYSQL_TYPE_FLOAT:
205 size = sizeof(float);
206 break;
207 case MYSQL_TYPE_DOUBLE:
208 size = sizeof(double);
209 break;
210 case MYSQL_TYPE_DATE:
211 case MYSQL_TYPE_TIME:
212 case MYSQL_TYPE_DATETIME:
213 case MYSQL_TYPE_TIMESTAMP:
214 size = sizeof(long);
215 break;
216 default:
217 size = sizeof(char);
218 }
219 return (_offset + size - 1) & ~(size - 1);
220}
221
223{
224 __DCL_ASSERT((Query::__fieldCount == 0)
225 && (__fields == NULL)
226 && (__outBINDs == NULL)
227 && (__outBuffer == NULL)
228 && (__stmt != NULL)
229 && (__stmt->field_count > 0)
230 );
231
232 Query::__fieldCount = __stmt->field_count;
233 size_t alloc = sizeof(MYSQL_BIND) * Query::__fieldCount;
234 __outBINDs = (MYSQL_BIND*)malloc(alloc);
235 if (!__outBINDs) {
237 return false;
238 }
239 memset(__outBINDs, 0, alloc);
240
241 // __fields를 위한 버퍼를 할당한다.
242 size_t offset = 0;
243 for (size_t i = 0; i < Query::__fieldCount; i++) {
244 MYSQL_FIELD& field = __stmt->fields[i];
245 offset = __TYPE_ALIGN(offset, field);
246 __DCL_TRACE3_N(L"offset [%4zd][%4zd] [%hs]\n",
247 offset, __TYPE_SIZE(field), field.name);
248 offset += __TYPE_SIZE(field);
249 }
250
251 __DCL_TRACE1_N(L"alloc [%zd]\n", offset);
252 // 모든 필드의 __TYPE_SIZE(field) == 0인 경우가 있을 수 있다.
253 // 모든 필드가 3000바이트 이상인 VARCHAR, VARBINARY, TEXT, BLOB이면
254 // offset의 값이 0이다.
255 if (offset > 0) {
256 __outBuffer = malloc(offset);
257 __DCL_ASSERT((size_t)__outBuffer % sizeof(void*) == 0);
258 if (__outBuffer == NULL) {
260 return false;
261 }
262 }
263
264 offset = 0;
265 for (size_t i = 0; i < Query::__fieldCount; i++) {
266 MYSQL_FIELD& field = __stmt->fields[i];
267 MYSQL_BIND& bind = __outBINDs[i];
268
269 offset = __TYPE_ALIGN(offset, field);
270 bind.buffer_length = __TYPE_SIZE(field);
271 bind.buffer_type = field.type;
272 bind.buffer = (char*)__outBuffer + offset;
273 offset += bind.buffer_length;
274
275 bind.length = &bind.length_value;
276 bind.is_null = &bind.is_null_value;
277 bind.error = &bind.error_value;
278 }
279
280 if (mysql_stmt_bind_result(__stmt, __outBINDs)) {
281 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
282 mysql_stmt_errno(__stmt),
283 mysql_stmt_error(__stmt)));
284 return false;
285 }
286
287 __fields = new MyField[Query::__fieldCount];
288 if (!__fields) {
290 return false;
291 }
292
293 for(unsigned int i = 0; i < Query::__fieldCount; i++) {
294 MYSQL_FIELD* field = __stmt->fields + i;
295 MYSQL_BIND* bind = __outBINDs + i;
296 if (!__fields[i].init(this, i, field, bind))
297 return false;
298 }
299
300 return true;
301}
302
303bool MyQuery::initParams(size_t _paramCount)
304{
305 __DCL_ASSERT((Query::__paramCount == 0)
306 && (__params == NULL)
307 && (__stmt != NULL)
308 && (__stmt->param_count > 0));
309 __DCL_ASSERT(__stmt->param_count == _paramCount);
310
311 Query::__paramCount = __stmt->param_count;
312 size_t alloc = sizeof(MYSQL_BIND) * Query::__paramCount;
313 __inBINDs = (MYSQL_BIND*)malloc(alloc);
314 if (!__inBINDs) {
316 return false;
317 }
318 memset(__inBINDs, 0, alloc);
319
320 __params = new MyParam[Query::__paramCount];
321 if (__params == NULL) {
323 return false;
324 }
325
326 for(unsigned int i = 0; i < Query::__paramCount; i++) {
327 MYSQL_BIND* bind = &__inBINDs[i];
328 bind->is_null = &bind->is_null_value;
329 if (!__params[i].init(this, i, bind))
330 return false;
331 }
332
333 return true;
334}
335
336bool MyQuery::__prepare(const char* _sql, size_t _sqllen,
337 size_t _paramCount)
338{
339 if(!reset())
340 return false;
341
342 if ((__stmt = mysql_stmt_init(conn()->connHandle())) == NULL) {
344 return false;
345 }
346
347 int r = mysql_stmt_prepare(__stmt, _sql, (unsigned long)_sqllen);
348 if (r) {
349 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
350 mysql_stmt_errno(__stmt),
351 mysql_stmt_error(__stmt)));
352 return false;
353 }
354
355#if __TRACE_THIS && 1
356 __DCL_TRACE2_N(L"state[%d] result.data[%p]\n",
357 __stmt->state, __stmt->result.data);
358 __DCL_TRACE4_N(L"params[%u][%p] fields[%u][%p]\n",
359 __stmt->param_count, __stmt->params,
360 __stmt->field_count, __stmt->fields);
361 for (unsigned int i = 0; i < __stmt->param_count; i++) {
362 MYSQL_BIND* e = &(__stmt->params[i]);
363 __DCL_TRACE4_N(L"param %02u buf[%p][%5u][%16ls]\n",
364 i, e->buffer, e->buffer_length, __dataTypeName(e->buffer_type, e->flags));
365 }
366 for (unsigned int i = 0; i < __stmt->field_count; i++) {
367 MYSQL_FIELD* e = &(__stmt->fields[i]);
368 __DCL_TRACE4_N(L"field %02u [%14hs][%10u][%16ls]\n",
369 i, e->name, e->length, __dataTypeName(e->type, e->flags));
370 }
371#endif
372
373 if (_paramCount > 0) {
374 if (!initParams(_paramCount))
375 return false;
376 }
377
378 return true;
379}
380
382{
383 __DCL_ASSERT(__stmt->param_count == Query::__paramCount);
384 // char*, byte_t*인 경우 데이터의 복사본을 유지하지 않는다.
385 // execute 전 매번 bind 한다.
386 if (Query::__paramCount) {
388 if (mysql_stmt_bind_param(__stmt, __inBINDs)) {
389 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
390 mysql_stmt_errno(__stmt),
391 mysql_stmt_error(__stmt)));
392 return false;
393 }
394 }
395
396 // 파라미터 값이 InputStream인 경우 mysql_stmt_send_long_data
397 for (size_t i = 0; i < Query::__paramCount; i++) {
398 if (!__params[i].onBeforeExecute()) {
399 return false;
400 }
401 }
402
403 if (mysql_stmt_execute(__stmt)) {
404 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
405 mysql_stmt_errno(__stmt),
406 mysql_stmt_error(__stmt)));
407 return false;
408 }
409
410 // UPDATE, INSERT, DELETE, etc, ...
411 Query::__affectedRows = mysql_stmt_affected_rows(__stmt);
412 __DCL_TRACE1_N(L"affectedRows[%lld]\n", Query::__affectedRows);
413
414 // execute 후 파라미터 값을 nil로 설정한다.
415 for (size_t i = 0; i < Query::__paramCount; i++) {
416 __params[i].onAfterExecute();
417 }
418
419#if __TRACE_THIS && 1
420 __DCL_TRACE2_N(L"state[%d] result.data[%p]\n",
421 __stmt->state, __stmt->result.data);
422 __DCL_TRACE2_N(L"fields[%u][%p]\n",
423 __stmt->field_count, __stmt->fields);
424 for (unsigned int i = 0; i < __stmt->field_count; i++) {
425 MYSQL_FIELD* e = &(__stmt->fields[i]);
426 __DCL_TRACE4_N(L"field %02u [%14hs][%10u][%16ls]\n",
427 i, e->name, e->length, __dataTypeName(e->type, e->flags));
428 }
429 if (conn()->connHandle()->server_status & SERVER_PS_OUT_PARAMS) {
430 __DCL_TRACE1_N(L"OUT/INOUT parameter [%u]\n", __stmt->field_count);
431 }
432#endif
433
434 // UPDATE, INSERT, DELETE만 eof 이어야 한다.
435 // 필드가 있는 경우, fetch에서 eof를 판별한다.
436 Query::__eof = __stmt->field_count == 0;
437
438 if (__stmt->field_count) {
439 if (!initFields()) {
440 return false;
441 }
442 if (conn()->storeResult()) {
443 __DCL_TRACE0_N(L"mysql_stmt_store_result\n");
444 if (mysql_stmt_store_result(__stmt)) {
445 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
446 mysql_stmt_errno(__stmt),
447 mysql_stmt_error(__stmt)));
448 return false;
449 }
450 }
451 }
452
453 return true;
454}
455
457{
458 __DCL_ASSERT(!Query::__eof);
459 int r = mysql_stmt_fetch(__stmt);
460 switch (r) {
461 case MYSQL_NO_DATA: {
462 // Success, no more data exists.
463 Query::__eof = true;
464 return true;
465 }
466 case MYSQL_DATA_TRUNCATED: {
467 // BLOB, MEDIUM_BLOB, LONG_BLOB은 mysql_stmt_fetch_column을 사용한다.
468 // 이 셋의 MYSQL_FIELD.length의 값은 0이다.
469 // Data truncation occurred.
470 // Check the error members of the MYSQL_BIND
471 __DCL_TRACE0_N(L"Warning!! MYSQL_DATA_TRUNCATED\n");
472
473#if __TRACE_THIS && 1
474 for (unsigned int i = 0; i < __stmt->field_count; i++) {
475 MYSQL_BIND& b = __stmt->bind[i];
476 __DCL_TRACE2(L"field[%u] [%u]\n", i, b.error ? *b.error : 0);
477 }
478#endif
479 }
480 case 0: {
481 // Success, the dta has been fetched to application data buffers.
482 return true;
483 }
484 case 1 :
485 // Error occurred. Error code and message can be obtained by calling
486 // mysql_stmt_errno() and mysql_stmt_error().
487 default:
488 ;
489 }
490
491 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
492 mysql_stmt_errno(__stmt),
493 mysql_stmt_error(__stmt)));
494 return false;
495}
496
498{
499 if (__fields) {
500 __DCL_ASSERT(Query::__fieldCount > 0);
501 delete[] __fields;
502 __fields = NULL;
503 Query::__fieldCount = 0;
504 }
505
506 if (__outBuffer) {
507 free(__outBuffer);
509 }
510
511 if (__outBINDs) {
512 free(__outBINDs);
514 }
515
516 __DCL_TRACE2_N(L"state[%d] result.data[%p]\n",
517 __stmt->state, __stmt->result.data);
518 if (__stmt->state >= MYSQL_STMT_USE_OR_STORE_CALLED) {
519 if (mysql_stmt_free_result(__stmt)) {
520 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
521 mysql_stmt_errno(__stmt),
522 mysql_stmt_error(__stmt)));
523 return false;
524 }
525 }
526
527 int r = mysql_stmt_next_result(__stmt);
528 if (r > 0) {
529 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
530 mysql_stmt_errno(__stmt),
531 mysql_stmt_error(__stmt)));
532 return false;
533 }
534#if __TRACE_THIS && 1
535 __DCL_TRACE2_N(L"state[%d] result.data[%p]\n",
536 __stmt->state, __stmt->result.data);
537 __DCL_TRACE3_N(L"next_[%d] fields[%u][%p]\n",
538 r, __stmt->field_count, __stmt->fields);
539 for (unsigned int i = 0; i < __stmt->field_count; i++) {
540 MYSQL_FIELD* e = &(__stmt->fields[i]);
541 __DCL_TRACE4_N(L"field %02u [%14hs][%10u][%16ls]\n",
542 i, e->name, e->length, __dataTypeName(e->type, e->flags));
543 }
544 if (conn()->connHandle()->server_status & SERVER_PS_OUT_PARAMS) {
545 __DCL_TRACE1_N(L"OUT/INOUT parameter [%u]\n", __stmt->field_count);
546 }
547#endif
548
549 // 필드가 있는 경우, fetch에서 eof를 판별한다.
550 Query::__eof = __stmt->field_count == 0;
551
552 if (__stmt->field_count) {
553 if (!initFields()) {
554 return false;
555 }
556 if (conn()->storeResult()) {
557 __DCL_TRACE0_N(L"mysql_stmt_store_result\n");
558 if (mysql_stmt_store_result(__stmt)) {
559 __SET_ERROR_MSG(ByteString::format("(%u) %hs",
560 mysql_stmt_errno(__stmt),
561 mysql_stmt_error(__stmt)));
562 return false;
563 }
564 }
565 }
566
567 return true;
568}
569
570bool MyQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
571{
572 __DCL_ASSERT(Query::__fieldCount > 0);
573 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
574 *_fieldHandleOut = &__fields[_index];
575 return true;
576}
577
578bool MyQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
579{
580 __DCL_ASSERT(Query::__paramCount > 0);
581 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
582 *_paramHandleOut = &__params[_index];
583 return true;
584}
585
586__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:340
#define __DCL_TRACE1_N(fmt, arg)
#define __DCL_TRACE2_N(fmt, arg1, arg2)
#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
#define __SET_ERROR_MSG(_message)
#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
Definition IFXField.cpp:41
const wchar_t * __dataTypeName(const ifx_sqlvar_t *_sqlvar)
Definition IFXField.cpp:290
#define __DCL_TRACE0_N(fmt)
Definition IFXField.cpp:37
#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
#define __DCL_TRACE2(fmt, arg1, arg2)
Definition Object.h:377
ByteString r
ByteBuffer * buf
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:150
void CharsetConvertException *size_t n
Definition SQLField.cpp:253
MYSQL_BIND * __inBINDs
Definition MyQuery.h:23
MyQuery(MyConnection *pConnection)
MyField * __fields
Definition MyQuery.h:28
virtual bool __execute()
Definition MyQuery.cpp:381
virtual bool __nextResult()
Definition MyQuery.cpp:497
MyParam * __params
Definition MyQuery.h:27
virtual bool __getParam(size_t _index, SQL::Param **_paramHandleOut)
Definition MyQuery.cpp:578
virtual ~MyQuery()
Definition MyQuery.cpp:55
bool initParams(size_t _paramCount)
Definition MyQuery.cpp:303
virtual void __destroy()
Definition MyQuery.cpp:81
virtual bool __fetch()
Definition MyQuery.cpp:456
void * __outBuffer
Definition MyQuery.h:25
MYSQL_STMT * __stmt
Definition MyQuery.h:22
bool reset()
Definition MyQuery.cpp:86
MYSQL_BIND * __outBINDs
Definition MyQuery.h:24
bool initFields()
Definition MyQuery.cpp:222
virtual bool __prepare(const char *_sql, size_t _sqllen, size_t _paramCount)
Definition MyQuery.cpp:336
virtual bool __getField(size_t _index, SQL::Field **_fieldHandleOut)
Definition MyQuery.cpp:570
@ eOutOfMemory
Definition SQLCore.h:24