ddb6e9b7722f3f9c99b18ffc208107b3dbab7d19
[public/netxms.git] / src / db / dbdrv / mysql / mysql.cpp
1 /*
2 ** MySQL Database Driver
3 ** Copyright (C) 2003-2012 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: mysql.cpp
20 **
21 **/
22
23 #include "mysqldrv.h"
24
25 DECLARE_DRIVER_HEADER("MYSQL")
26
27 /**
28 * Update error message from given source
29 */
30 static void UpdateErrorMessage(const char *source, WCHAR *errorText)
31 {
32 if (errorText != NULL)
33 {
34 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, source, -1, errorText, DBDRV_MAX_ERROR_TEXT);
35 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
36 RemoveTrailingCRLFW(errorText);
37 }
38 }
39
40 /**
41 * Update buffer length in DrvPrepareStringW
42 */
43 #define UPDATE_LENGTH \
44 len++; \
45 if (len >= bufferSize - 1) \
46 { \
47 bufferSize += 128; \
48 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); \
49 }
50
51 /**
52 * Prepare string for using in SQL query - enclose in quotes and escape as needed
53 * (wide string version)
54 */
55 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
56 {
57 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
58 int bufferSize = len + 128;
59 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
60 out[0] = _T('\'');
61
62 const WCHAR *src = str;
63 int outPos;
64 for(outPos = 1; *src != 0; src++)
65 {
66 switch(*src)
67 {
68 case L'\'':
69 out[outPos++] = L'\'';
70 out[outPos++] = L'\'';
71 UPDATE_LENGTH;
72 break;
73 case L'\r':
74 out[outPos++] = L'\\';
75 out[outPos++] = L'\r';
76 UPDATE_LENGTH;
77 break;
78 case L'\n':
79 out[outPos++] = L'\\';
80 out[outPos++] = L'\n';
81 UPDATE_LENGTH;
82 break;
83 case L'\b':
84 out[outPos++] = L'\\';
85 out[outPos++] = L'\b';
86 UPDATE_LENGTH;
87 break;
88 case L'\t':
89 out[outPos++] = L'\\';
90 out[outPos++] = L'\t';
91 UPDATE_LENGTH;
92 break;
93 case 26:
94 out[outPos++] = L'\\';
95 out[outPos++] = L'Z';
96 break;
97 case L'\\':
98 out[outPos++] = L'\\';
99 out[outPos++] = L'\\';
100 UPDATE_LENGTH;
101 break;
102 default:
103 out[outPos++] = *src;
104 break;
105 }
106 }
107 out[outPos++] = L'\'';
108 out[outPos++] = 0;
109
110 return out;
111 }
112
113 #undef UPDATE_LENGTH
114
115 /**
116 * Update buffer length in DrvPrepareStringA
117 */
118 #define UPDATE_LENGTH \
119 len++; \
120 if (len >= bufferSize - 1) \
121 { \
122 bufferSize += 128; \
123 out = (char *)realloc(out, bufferSize); \
124 }
125
126 /**
127 * Prepare string for using in SQL query - enclose in quotes and escape as needed
128 * (multibyte string version)
129 */
130 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
131 {
132 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
133 int bufferSize = len + 128;
134 char *out = (char *)malloc(bufferSize);
135 out[0] = _T('\'');
136
137 const char *src = str;
138 int outPos;
139 for(outPos = 1; *src != 0; src++)
140 {
141 switch(*src)
142 {
143 case '\'':
144 out[outPos++] = '\'';
145 out[outPos++] = '\'';
146 UPDATE_LENGTH;
147 break;
148 case '\r':
149 out[outPos++] = '\\';
150 out[outPos++] = '\r';
151 UPDATE_LENGTH;
152 break;
153 case '\n':
154 out[outPos++] = '\\';
155 out[outPos++] = '\n';
156 UPDATE_LENGTH;
157 break;
158 case '\b':
159 out[outPos++] = '\\';
160 out[outPos++] = '\b';
161 UPDATE_LENGTH;
162 break;
163 case '\t':
164 out[outPos++] = '\\';
165 out[outPos++] = '\t';
166 UPDATE_LENGTH;
167 break;
168 case 26:
169 out[outPos++] = '\\';
170 out[outPos++] = 'Z';
171 break;
172 case '\\':
173 out[outPos++] = '\\';
174 out[outPos++] = '\\';
175 UPDATE_LENGTH;
176 break;
177 default:
178 out[outPos++] = *src;
179 break;
180 }
181 }
182 out[outPos++] = '\'';
183 out[outPos++] = 0;
184
185 return out;
186 }
187
188 #undef UPDATE_LENGTH
189
190 /**
191 * Initialize driver
192 */
193 extern "C" BOOL EXPORT DrvInit(const char *cmdLine)
194 {
195 return mysql_library_init(0, NULL, NULL) == 0;
196 }
197
198 /**
199 * Unload handler
200 */
201 extern "C" void EXPORT DrvUnload()
202 {
203 mysql_library_end();
204 }
205
206 /**
207 * Connect to database
208 */
209 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *szHost, const char *szLogin, const char *szPassword,
210 const char *szDatabase, const char *schema, WCHAR *errorText)
211 {
212 MYSQL *pMySQL;
213 MYSQL_CONN *pConn;
214 const char *pHost = szHost;
215 const char *pSocket = NULL;
216
217 pMySQL = mysql_init(NULL);
218 if (pMySQL == NULL)
219 {
220 wcscpy(errorText, L"Insufficient memory to allocate connection handle");
221 return NULL;
222 }
223
224 pSocket = strstr(szHost, "socket:");
225 if (pSocket != NULL)
226 {
227 pHost = NULL;
228 pSocket += 7;
229 }
230
231 if (!mysql_real_connect(
232 pMySQL, // MYSQL *
233 pHost, // host
234 szLogin[0] == 0 ? NULL : szLogin, // user
235 (szPassword[0] == 0 || szLogin[0] == 0) ? NULL : szPassword, // pass
236 szDatabase, // DB Name
237 0, // use default port
238 pSocket, // char * - unix socket
239 0 // flags
240 ))
241 {
242 UpdateErrorMessage(mysql_error(pMySQL), errorText);
243 mysql_close(pMySQL);
244 return NULL;
245 }
246
247 pConn = (MYSQL_CONN *)malloc(sizeof(MYSQL_CONN));
248 pConn->pMySQL = pMySQL;
249 pConn->mutexQueryLock = MutexCreate();
250
251 // Switch to UTF-8 encoding
252 mysql_set_character_set(pMySQL, "utf8");
253
254 return (DBDRV_CONNECTION)pConn;
255 }
256
257 /**
258 * Disconnect from database
259 */
260 extern "C" void EXPORT DrvDisconnect(MYSQL_CONN *pConn)
261 {
262 if (pConn != NULL)
263 {
264 mysql_close(pConn->pMySQL);
265 MutexDestroy(pConn->mutexQueryLock);
266 free(pConn);
267 }
268 }
269
270 /**
271 * Prepare statement
272 */
273 extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
274 {
275 MYSQL_STATEMENT *result = NULL;
276
277 MutexLock(pConn->mutexQueryLock);
278 MYSQL_STMT *stmt = mysql_stmt_init(pConn->pMySQL);
279 if (stmt != NULL)
280 {
281 char *pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
282 int rc = mysql_stmt_prepare(stmt, pszQueryUTF8, (unsigned long)strlen(pszQueryUTF8));
283 if (rc == 0)
284 {
285 result = (MYSQL_STATEMENT *)malloc(sizeof(MYSQL_STATEMENT));
286 result->connection = pConn;
287 result->statement = stmt;
288 result->paramCount = (int)mysql_stmt_param_count(stmt);
289 result->bindings = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * result->paramCount);
290 memset(result->bindings, 0, sizeof(MYSQL_BIND) * result->paramCount);
291 result->lengthFields = (unsigned long *)malloc(sizeof(unsigned long) * result->paramCount);
292 memset(result->lengthFields, 0, sizeof(unsigned long) * result->paramCount);
293 result->buffers = new Array(result->paramCount, 16, true);
294 *pdwError = DBERR_SUCCESS;
295 }
296 else
297 {
298 int nErr = mysql_errno(pConn->pMySQL);
299 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR)
300 {
301 *pdwError = DBERR_CONNECTION_LOST;
302 }
303 else
304 {
305 *pdwError = DBERR_OTHER_ERROR;
306 }
307 UpdateErrorMessage(mysql_stmt_error(stmt), errorText);
308 mysql_stmt_close(stmt);
309 }
310 free(pszQueryUTF8);
311 }
312 else
313 {
314 *pdwError = DBERR_OTHER_ERROR;
315 UpdateErrorMessage("Call to mysql_stmt_init failed", errorText);
316 }
317 MutexUnlock(pConn->mutexQueryLock);
318 return result;
319 }
320
321 /**
322 * Bind parameter to prepared statement
323 */
324 extern "C" void EXPORT DrvBind(MYSQL_STATEMENT *hStmt, int pos, int sqlType, int cType, void *buffer, int allocType)
325 {
326 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
327
328 if ((pos < 1) || (pos > hStmt->paramCount))
329 return;
330 MYSQL_BIND *b = &hStmt->bindings[pos - 1];
331
332 if (cType == DB_CTYPE_STRING)
333 {
334 b->buffer = UTF8StringFromWideString((WCHAR *)buffer);
335 hStmt->buffers->add(b->buffer);
336 if (allocType == DB_BIND_DYNAMIC)
337 free(buffer);
338 b->buffer_length = (unsigned long)strlen((char *)b->buffer) + 1;
339 hStmt->lengthFields[pos - 1] = b->buffer_length - 1;
340 b->length = &hStmt->lengthFields[pos - 1];
341 b->buffer_type = MYSQL_TYPE_STRING;
342 }
343 else
344 {
345 switch(allocType)
346 {
347 case DB_BIND_STATIC:
348 b->buffer = buffer;
349 break;
350 case DB_BIND_DYNAMIC:
351 b->buffer = buffer;
352 hStmt->buffers->add(buffer);
353 break;
354 case DB_BIND_TRANSIENT:
355 b->buffer = nx_memdup(buffer, bufferSize[cType]);
356 hStmt->buffers->add(b->buffer);
357 break;
358 default:
359 return; // Invalid call
360 }
361
362 switch(cType)
363 {
364 case DB_CTYPE_UINT32:
365 b->is_unsigned = TRUE;
366 case DB_CTYPE_INT32:
367 b->buffer_type = MYSQL_TYPE_LONG;
368 break;
369 case DB_CTYPE_UINT64:
370 b->is_unsigned = TRUE;
371 case DB_CTYPE_INT64:
372 b->buffer_type = MYSQL_TYPE_LONGLONG;
373 break;
374 case DB_CTYPE_DOUBLE:
375 b->buffer_type = MYSQL_TYPE_DOUBLE;
376 break;
377 }
378 }
379 }
380
381 /**
382 * Execute prepared statement
383 */
384 extern "C" DWORD EXPORT DrvExecute(MYSQL_CONN *pConn, MYSQL_STATEMENT *hStmt, WCHAR *errorText)
385 {
386 DWORD dwResult;
387
388 MutexLock(pConn->mutexQueryLock);
389
390 if (mysql_stmt_bind_param(hStmt->statement, hStmt->bindings) == 0)
391 {
392 if (mysql_stmt_execute(hStmt->statement) == 0)
393 {
394 dwResult = DBERR_SUCCESS;
395 }
396 else
397 {
398 int nErr = mysql_errno(pConn->pMySQL);
399 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR)
400 {
401 dwResult = DBERR_CONNECTION_LOST;
402 }
403 else
404 {
405 dwResult = DBERR_OTHER_ERROR;
406 }
407 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
408 }
409 }
410 else
411 {
412 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
413 dwResult = DBERR_OTHER_ERROR;
414 }
415
416 MutexUnlock(pConn->mutexQueryLock);
417 return dwResult;
418 }
419
420 /**
421 * Destroy prepared statement
422 */
423 extern "C" void EXPORT DrvFreeStatement(MYSQL_STATEMENT *hStmt)
424 {
425 if (hStmt == NULL)
426 return;
427
428 MutexLock(hStmt->connection->mutexQueryLock);
429 mysql_stmt_close(hStmt->statement);
430 MutexUnlock(hStmt->connection->mutexQueryLock);
431 delete hStmt->buffers;
432 safe_free(hStmt->bindings);
433 safe_free(hStmt->lengthFields);
434 free(hStmt);
435 }
436
437 /**
438 * Perform actual non-SELECT query
439 */
440 static DWORD DrvQueryInternal(MYSQL_CONN *pConn, const char *pszQuery, WCHAR *errorText)
441 {
442 DWORD dwRet = DBERR_INVALID_HANDLE;
443
444 MutexLock(pConn->mutexQueryLock);
445 if (mysql_query(pConn->pMySQL, pszQuery) == 0)
446 {
447 dwRet = DBERR_SUCCESS;
448 if (errorText != NULL)
449 *errorText = 0;
450 }
451 else
452 {
453 int nErr = mysql_errno(pConn->pMySQL);
454 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
455 {
456 dwRet = DBERR_CONNECTION_LOST;
457 }
458 else
459 {
460 dwRet = DBERR_OTHER_ERROR;
461 }
462 UpdateErrorMessage(mysql_error(pConn->pMySQL), errorText);
463 }
464
465 MutexUnlock(pConn->mutexQueryLock);
466 return dwRet;
467 }
468
469 /**
470 * Perform non-SELECT query
471 */
472 extern "C" DWORD EXPORT DrvQuery(MYSQL_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText)
473 {
474 DWORD dwRet;
475 char *pszQueryUTF8;
476
477 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
478 dwRet = DrvQueryInternal(pConn, pszQueryUTF8, errorText);
479 free(pszQueryUTF8);
480 return dwRet;
481 }
482
483 /**
484 * Perform SELECT query
485 */
486 extern "C" DBDRV_RESULT EXPORT DrvSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
487 {
488 MYSQL_RESULT *result = NULL;
489 char *pszQueryUTF8;
490
491 if (pConn == NULL)
492 {
493 *pdwError = DBERR_INVALID_HANDLE;
494 return NULL;
495 }
496
497 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
498 MutexLock(pConn->mutexQueryLock);
499 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
500 {
501 result = (MYSQL_RESULT *)malloc(sizeof(MYSQL_RESULT));
502 result->isPreparedStatement = false;
503 result->resultSet = mysql_store_result(pConn->pMySQL);
504 *pdwError = DBERR_SUCCESS;
505 if (errorText != NULL)
506 *errorText = 0;
507 }
508 else
509 {
510 int nErr = mysql_errno(pConn->pMySQL);
511 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
512 {
513 *pdwError = DBERR_CONNECTION_LOST;
514 }
515 else
516 {
517 *pdwError = DBERR_OTHER_ERROR;
518 }
519 UpdateErrorMessage(mysql_error(pConn->pMySQL), errorText);
520 }
521
522 MutexUnlock(pConn->mutexQueryLock);
523 free(pszQueryUTF8);
524 return result;
525 }
526
527 /**
528 * Perform SELECT query using prepared statement
529 */
530 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(MYSQL_CONN *pConn, MYSQL_STATEMENT *hStmt, DWORD *pdwError, WCHAR *errorText)
531 {
532 MYSQL_RESULT *result = NULL;
533
534 if (pConn == NULL)
535 {
536 *pdwError = DBERR_INVALID_HANDLE;
537 return NULL;
538 }
539
540 MutexLock(pConn->mutexQueryLock);
541
542 if (mysql_stmt_bind_param(hStmt->statement, hStmt->bindings) == 0)
543 {
544 if (mysql_stmt_execute(hStmt->statement) == 0)
545 {
546 result = (MYSQL_RESULT *)malloc(sizeof(MYSQL_RESULT));
547 result->isPreparedStatement = true;
548 result->statement = hStmt->statement;
549 result->resultSet = mysql_stmt_result_metadata(hStmt->statement);
550 if (result->resultSet != NULL)
551 {
552 result->numColumns = mysql_num_fields(result->resultSet);
553
554 result->lengthFields = (unsigned long *)malloc(sizeof(unsigned long) * result->numColumns);
555 memset(result->lengthFields, 0, sizeof(unsigned long) * result->numColumns);
556
557 result->bindings = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * result->numColumns);
558 memset(result->bindings, 0, sizeof(MYSQL_BIND) * result->numColumns);
559 for(int i = 0; i < result->numColumns; i++)
560 {
561 result->bindings[i].buffer_type = MYSQL_TYPE_STRING;
562 result->bindings[i].length = &result->lengthFields[i];
563 }
564
565 mysql_stmt_bind_result(hStmt->statement, result->bindings);
566
567 if (mysql_stmt_store_result(hStmt->statement) == 0)
568 {
569 result->numRows = (int)mysql_stmt_num_rows(hStmt->statement);
570 result->currentRow = -1;
571 *pdwError = DBERR_SUCCESS;
572 }
573 else
574 {
575 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
576 *pdwError = DBERR_OTHER_ERROR;
577 mysql_free_result(result->resultSet);
578 free(result->bindings);
579 free(result->lengthFields);
580 free(result);
581 result = NULL;
582 }
583 }
584 else
585 {
586 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
587 *pdwError = DBERR_OTHER_ERROR;
588 free(result);
589 result = NULL;
590 }
591 }
592 else
593 {
594 int nErr = mysql_errno(pConn->pMySQL);
595 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR)
596 {
597 *pdwError = DBERR_CONNECTION_LOST;
598 }
599 else
600 {
601 *pdwError = DBERR_OTHER_ERROR;
602 }
603 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
604 }
605 }
606 else
607 {
608 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
609 *pdwError = DBERR_OTHER_ERROR;
610 }
611
612 MutexUnlock(pConn->mutexQueryLock);
613 return result;
614 }
615
616 /**
617 * Get field length from result
618 */
619 extern "C" LONG EXPORT DrvGetFieldLength(MYSQL_RESULT *hResult, int iRow, int iColumn)
620 {
621 if (hResult->isPreparedStatement)
622 {
623 if ((iRow < 0) || (iRow >= hResult->numRows) ||
624 (iColumn < 0) || (iColumn >= hResult->numColumns))
625 return -1;
626
627 if (hResult->currentRow != iRow)
628 {
629 mysql_stmt_data_seek(hResult->statement, iRow);
630 mysql_stmt_fetch(hResult->statement);
631 hResult->currentRow = iRow;
632 }
633 return (LONG)hResult->lengthFields[iColumn];
634 }
635 else
636 {
637 mysql_data_seek(hResult->resultSet, iRow);
638 MYSQL_ROW row = mysql_fetch_row(hResult->resultSet);
639 return (row == NULL) ? (LONG)-1 : ((row[iColumn] == NULL) ? -1 : (LONG)strlen(row[iColumn]));
640 }
641 }
642
643 /**
644 * Get field value from result
645 */
646 extern "C" WCHAR EXPORT *DrvGetField(MYSQL_RESULT *hResult, int iRow, int iColumn, WCHAR *pBuffer, int nBufSize)
647 {
648 WCHAR *pRet = NULL;
649
650 if (hResult->isPreparedStatement)
651 {
652 MYSQL_BIND b;
653 unsigned long l = 0;
654 my_bool isNull;
655
656 if ((iRow < 0) || (iRow >= hResult->numRows) ||
657 (iColumn < 0) || (iColumn >= hResult->numColumns))
658 return NULL;
659
660 if (hResult->currentRow != iRow)
661 {
662 mysql_stmt_data_seek(hResult->statement, iRow);
663 mysql_stmt_fetch(hResult->statement);
664 hResult->currentRow = iRow;
665 }
666
667 memset(&b, 0, sizeof(MYSQL_BIND));
668 #if HAVE_ALLOCA || defined(_WIN32)
669 b.buffer = alloca(hResult->lengthFields[iColumn] + 1);
670 #else
671 b.buffer = malloc(hResult->lengthFields[iColumn] + 1);
672 #endif
673 b.buffer_length = hResult->lengthFields[iColumn] + 1;
674 b.buffer_type = MYSQL_TYPE_STRING;
675 b.length = &l;
676 b.is_null = &isNull;
677 int rc;
678 if ((rc=mysql_stmt_fetch_column(hResult->statement, &b, iColumn, 0)) == 0)
679 {
680 ((char *)b.buffer)[l] = 0;
681 MultiByteToWideChar(CP_UTF8, 0, (char *)b.buffer, -1, pBuffer, nBufSize);
682 pBuffer[nBufSize - 1] = 0;
683 pRet = pBuffer;
684 }
685 #if !HAVE_ALLOCA && !defined(_WIN32)
686 free(b.buffer);
687 #endif
688 }
689 else
690 {
691 mysql_data_seek(hResult->resultSet, iRow);
692 MYSQL_ROW row = mysql_fetch_row(hResult->resultSet);
693 if (row != NULL)
694 {
695 if (row[iColumn] != NULL)
696 {
697 MultiByteToWideChar(CP_UTF8, 0, row[iColumn], -1, pBuffer, nBufSize);
698 pBuffer[nBufSize - 1] = 0;
699 pRet = pBuffer;
700 }
701 }
702 }
703 return pRet;
704 }
705
706
707 //
708 // Get number of rows in result
709 //
710
711 extern "C" int EXPORT DrvGetNumRows(MYSQL_RESULT *hResult)
712 {
713 return (hResult != NULL) ? (int)(hResult->isPreparedStatement ? mysql_stmt_num_rows(hResult->statement) : mysql_num_rows(hResult->resultSet)) : 0;
714 }
715
716
717 //
718 // Get column count in query result
719 //
720
721 extern "C" int EXPORT DrvGetColumnCount(MYSQL_RESULT *hResult)
722 {
723 return (hResult != NULL) ? (int)mysql_num_fields(hResult->resultSet) : 0;
724 }
725
726
727 //
728 // Get column name in query result
729 //
730
731 extern "C" const char EXPORT *DrvGetColumnName(MYSQL_RESULT *hResult, int column)
732 {
733 MYSQL_FIELD *field;
734
735 if (hResult == NULL)
736 return NULL;
737
738 field = mysql_fetch_field_direct(hResult->resultSet, column);
739 return (field != NULL) ? field->name : NULL;
740 }
741
742
743 //
744 // Free SELECT results
745 //
746
747 extern "C" void EXPORT DrvFreeResult(MYSQL_RESULT *hResult)
748 {
749 if (hResult == NULL)
750 return;
751
752 if (hResult->isPreparedStatement)
753 {
754 safe_free(hResult->bindings);
755 safe_free(hResult->lengthFields);
756 }
757
758 mysql_free_result(hResult->resultSet);
759 free(hResult);
760 }
761
762
763 //
764 // Perform asynchronous SELECT query
765 //
766
767 extern "C" DBDRV_ASYNC_RESULT EXPORT DrvAsyncSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery,
768 DWORD *pdwError, WCHAR *errorText)
769 {
770 MYSQL_ASYNC_RESULT *pResult = NULL;
771 char *pszQueryUTF8;
772
773 if (pConn == NULL)
774 {
775 *pdwError = DBERR_INVALID_HANDLE;
776 return NULL;
777 }
778
779 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
780 MutexLock(pConn->mutexQueryLock);
781 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
782 {
783 pResult = (MYSQL_ASYNC_RESULT *)malloc(sizeof(MYSQL_ASYNC_RESULT));
784 pResult->pConn = pConn;
785 pResult->pHandle = mysql_use_result(pConn->pMySQL);
786 if (pResult->pHandle != NULL)
787 {
788 pResult->bNoMoreRows = FALSE;
789 pResult->iNumCols = mysql_num_fields(pResult->pHandle);
790 pResult->pCurrRow = NULL;
791 pResult->pulColLengths = (unsigned long *)malloc(sizeof(unsigned long) * pResult->iNumCols);
792 }
793 else
794 {
795 free(pResult);
796 pResult = NULL;
797 }
798
799 *pdwError = DBERR_SUCCESS;
800 if (errorText != NULL)
801 *errorText = 0;
802 }
803 else
804 {
805 int nErr = mysql_errno(pConn->pMySQL);
806 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
807 {
808 *pdwError = DBERR_CONNECTION_LOST;
809 }
810 else
811 {
812 *pdwError = DBERR_OTHER_ERROR;
813 }
814
815 if (errorText != NULL)
816 {
817 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mysql_error(pConn->pMySQL), -1, errorText, DBDRV_MAX_ERROR_TEXT);
818 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
819 RemoveTrailingCRLFW(errorText);
820 }
821 }
822
823 if (pResult == NULL)
824 {
825 MutexUnlock(pConn->mutexQueryLock);
826 }
827 free(pszQueryUTF8);
828
829 return pResult;
830 }
831
832
833 //
834 // Fetch next result line from asynchronous SELECT results
835 //
836
837 extern "C" BOOL EXPORT DrvFetch(MYSQL_ASYNC_RESULT *hResult)
838 {
839 BOOL bResult = TRUE;
840
841 if (hResult == NULL)
842 {
843 bResult = FALSE;
844 }
845 else
846 {
847 // Try to fetch next row from server
848 hResult->pCurrRow = mysql_fetch_row(hResult->pHandle);
849 if (hResult->pCurrRow == NULL)
850 {
851 hResult->bNoMoreRows = TRUE;
852 bResult = FALSE;
853 MutexUnlock(hResult->pConn->mutexQueryLock);
854 }
855 else
856 {
857 unsigned long *pLen;
858
859 // Get column lengths for current row
860 pLen = mysql_fetch_lengths(hResult->pHandle);
861 if (pLen != NULL)
862 {
863 memcpy(hResult->pulColLengths, pLen, sizeof(unsigned long) * hResult->iNumCols);
864 }
865 else
866 {
867 memset(hResult->pulColLengths, 0, sizeof(unsigned long) * hResult->iNumCols);
868 }
869 }
870 }
871 return bResult;
872 }
873
874
875 //
876 // Get field length from async query result result
877 //
878
879 extern "C" LONG EXPORT DrvGetFieldLengthAsync(MYSQL_ASYNC_RESULT *hResult, int iColumn)
880 {
881 // Check if we have valid result handle
882 if (hResult == NULL)
883 return 0;
884
885 // Check if there are valid fetched row
886 if (hResult->bNoMoreRows || (hResult->pCurrRow == NULL))
887 return 0;
888
889 // Check if column number is valid
890 if ((iColumn < 0) || (iColumn >= hResult->iNumCols))
891 return 0;
892
893 return hResult->pulColLengths[iColumn];
894 }
895
896
897 //
898 // Get field from current row in async query result
899 //
900
901 extern "C" WCHAR EXPORT *DrvGetFieldAsync(DBDRV_ASYNC_RESULT hResult, int iColumn,
902 WCHAR *pBuffer, int iBufSize)
903 {
904 int iLen;
905
906 // Check if we have valid result handle
907 if (hResult == NULL)
908 return NULL;
909
910 // Check if there are valid fetched row
911 if ((((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows) ||
912 (((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow == NULL))
913 return NULL;
914
915 // Check if column number is valid
916 if ((iColumn < 0) || (iColumn >= ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols))
917 return NULL;
918
919 // Now get column data
920 iLen = min((int)((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths[iColumn], iBufSize - 1);
921 if (iLen > 0)
922 {
923 MultiByteToWideChar(CP_UTF8, 0, ((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow[iColumn],
924 iLen, pBuffer, iBufSize);
925 }
926 pBuffer[iLen] = 0;
927
928 return pBuffer;
929 }
930
931
932 //
933 // Get column count in async query result
934 //
935
936 extern "C" int EXPORT DrvGetColumnCountAsync(DBDRV_ASYNC_RESULT hResult)
937 {
938 return ((hResult != NULL) && (((MYSQL_ASYNC_RESULT *)hResult)->pHandle != NULL))? (int)mysql_num_fields(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) : 0;
939 }
940
941
942 //
943 // Get column name in async query result
944 //
945
946 extern "C" const char EXPORT *DrvGetColumnNameAsync(DBDRV_ASYNC_RESULT hResult, int column)
947 {
948 MYSQL_FIELD *field;
949
950 if ((hResult == NULL) || (((MYSQL_ASYNC_RESULT *)hResult)->pHandle == NULL))
951 return NULL;
952
953 field = mysql_fetch_field_direct(((MYSQL_ASYNC_RESULT *)hResult)->pHandle, column);
954 return (field != NULL) ? field->name : NULL;
955 }
956
957
958 //
959 // Destroy result of async query
960 //
961
962 extern "C" void EXPORT DrvFreeAsyncResult(DBDRV_ASYNC_RESULT hResult)
963 {
964 if (hResult != NULL)
965 {
966 // Check if all result rows fetched
967 if (!((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows)
968 {
969 // Fetch remaining rows
970 while(mysql_fetch_row(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) != NULL);
971
972 // Now we are ready for next query, so unlock query mutex
973 MutexUnlock(((MYSQL_ASYNC_RESULT *)hResult)->pConn->mutexQueryLock);
974 }
975
976 // Free allocated memory
977 mysql_free_result(((MYSQL_ASYNC_RESULT *)hResult)->pHandle);
978 safe_free(((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths);
979 free(hResult);
980 }
981 }
982
983
984 //
985 // Begin transaction
986 //
987
988 extern "C" DWORD EXPORT DrvBegin(MYSQL_CONN *pConn)
989 {
990 return DrvQueryInternal(pConn, "BEGIN", NULL);
991 }
992
993
994 //
995 // Commit transaction
996 //
997
998 extern "C" DWORD EXPORT DrvCommit(MYSQL_CONN *pConn)
999 {
1000 return DrvQueryInternal(pConn, "COMMIT", NULL);
1001 }
1002
1003
1004 //
1005 // Rollback transaction
1006 //
1007
1008 extern "C" DWORD EXPORT DrvRollback(MYSQL_CONN *pConn)
1009 {
1010 return DrvQueryInternal(pConn, "ROLLBACK", NULL);
1011 }
1012
1013
1014 //
1015 // DLL Entry point
1016 //
1017
1018 #ifdef _WIN32
1019
1020 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1021 {
1022 if (dwReason == DLL_PROCESS_ATTACH)
1023 DisableThreadLibraryCalls(hInstance);
1024 return TRUE;
1025 }
1026
1027 #endif