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