minor changes
[public/netxms.git] / src / db / dbdrv / mysql / mysql.cpp
CommitLineData
5039dede
AK
1/*
2** MySQL Database Driver
de1d708f 3** Copyright (C) 2003-2012 Victor Kirhenshtein
5039dede
AK
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
c94bb5aa
VK
25DECLARE_DRIVER_HEADER("MYSQL")
26
7aad6641
VK
27/**
28 * Update error message from given source
29 */
c94bb5aa
VK
30static 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}
5039dede 39
7aad6641
VK
40/**
41 * Update buffer length in DrvPrepareStringW
42 */
643c9dcb
VK
43#define UPDATE_LENGTH \
44 len++; \
45 if (len >= bufferSize - 1) \
46 { \
47 bufferSize += 128; \
fe1d4002 48 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); \
643c9dcb
VK
49 }
50
7aad6641
VK
51/**
52 * Prepare string for using in SQL query - enclose in quotes and escape as needed
53 * (wide string version)
54 */
fe1d4002 55extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
643c9dcb 56{
fe1d4002 57 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
643c9dcb 58 int bufferSize = len + 128;
fe1d4002 59 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
643c9dcb
VK
60 out[0] = _T('\'');
61
fe1d4002 62 const WCHAR *src = str;
643c9dcb 63 int outPos;
66827adf 64 for(outPos = 1; *src != 0; src++)
643c9dcb
VK
65 {
66 switch(*src)
67 {
fe1d4002
VK
68 case L'\'':
69 out[outPos++] = L'\'';
70 out[outPos++] = L'\'';
643c9dcb
VK
71 UPDATE_LENGTH;
72 break;
fe1d4002
VK
73 case L'\r':
74 out[outPos++] = L'\\';
75 out[outPos++] = L'\r';
643c9dcb
VK
76 UPDATE_LENGTH;
77 break;
fe1d4002
VK
78 case L'\n':
79 out[outPos++] = L'\\';
80 out[outPos++] = L'\n';
643c9dcb
VK
81 UPDATE_LENGTH;
82 break;
fe1d4002
VK
83 case L'\b':
84 out[outPos++] = L'\\';
85 out[outPos++] = L'\b';
643c9dcb
VK
86 UPDATE_LENGTH;
87 break;
fe1d4002
VK
88 case L'\t':
89 out[outPos++] = L'\\';
90 out[outPos++] = L'\t';
643c9dcb
VK
91 UPDATE_LENGTH;
92 break;
93 case 26:
fe1d4002
VK
94 out[outPos++] = L'\\';
95 out[outPos++] = L'Z';
643c9dcb 96 break;
fe1d4002
VK
97 case L'\\':
98 out[outPos++] = L'\\';
99 out[outPos++] = L'\\';
643c9dcb
VK
100 UPDATE_LENGTH;
101 break;
102 default:
103 out[outPos++] = *src;
104 break;
105 }
106 }
fe1d4002
VK
107 out[outPos++] = L'\'';
108 out[outPos++] = 0;
109
110 return out;
111}
112
113#undef UPDATE_LENGTH
7aad6641
VK
114
115/**
116 * Update buffer length in DrvPrepareStringA
117 */
fe1d4002
VK
118#define UPDATE_LENGTH \
119 len++; \
120 if (len >= bufferSize - 1) \
121 { \
122 bufferSize += 128; \
123 out = (char *)realloc(out, bufferSize); \
124 }
125
7aad6641
VK
126/**
127 * Prepare string for using in SQL query - enclose in quotes and escape as needed
128 * (multibyte string version)
129 */
fe1d4002
VK
130extern "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;
66827adf 139 for(outPos = 1; *src != 0; src++)
fe1d4002
VK
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++] = '\'';
643c9dcb
VK
183 out[outPos++] = 0;
184
185 return out;
186}
187
188#undef UPDATE_LENGTH
189
7aad6641
VK
190/**
191 * Initialize driver
192 */
465b3f2d 193extern "C" BOOL EXPORT DrvInit(const char *cmdLine)
5039dede 194{
fa88627b 195 return mysql_library_init(0, NULL, NULL) == 0;
5039dede
AK
196}
197
7aad6641
VK
198/**
199 * Unload handler
200 */
08b214c6 201extern "C" void EXPORT DrvUnload()
5039dede 202{
fa88627b 203 mysql_library_end();
5039dede
AK
204}
205
7aad6641
VK
206/**
207 * Connect to database
208 */
465b3f2d 209extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *szHost, const char *szLogin, const char *szPassword,
f3c30cf5 210 const char *szDatabase, const char *schema, WCHAR *errorText)
5039dede
AK
211{
212 MYSQL *pMySQL;
213 MYSQL_CONN *pConn;
465b3f2d
VK
214 const char *pHost = szHost;
215 const char *pSocket = NULL;
5039dede
AK
216
217 pMySQL = mysql_init(NULL);
218 if (pMySQL == NULL)
219 {
465b3f2d 220 wcscpy(errorText, L"Insufficient memory to allocate connection handle");
5039dede
AK
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 {
c94bb5aa 242 UpdateErrorMessage(mysql_error(pMySQL), errorText);
5039dede
AK
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
643c9dcb 252 mysql_set_character_set(pMySQL, "utf8");
5039dede 253
b8c1ec69 254 return (DBDRV_CONNECTION)pConn;
5039dede
AK
255}
256
7aad6641
VK
257/**
258 * Disconnect from database
259 */
5039dede
AK
260extern "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
7aad6641
VK
270/**
271 * Prepare statement
272 */
de1d708f 273extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
c94bb5aa
VK
274{
275 MYSQL_STATEMENT *result = NULL;
276
c17f6cbc 277 MutexLock(pConn->mutexQueryLock);
c94bb5aa
VK
278 MYSQL_STMT *stmt = mysql_stmt_init(pConn->pMySQL);
279 if (stmt != NULL)
280 {
281 char *pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
ef856d02 282 int rc = mysql_stmt_prepare(stmt, pszQueryUTF8, (unsigned long)strlen(pszQueryUTF8));
c94bb5aa
VK
283 if (rc == 0)
284 {
285 result = (MYSQL_STATEMENT *)malloc(sizeof(MYSQL_STATEMENT));
fa88627b 286 result->connection = pConn;
c94bb5aa
VK
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);
140b8ada
VK
291 result->lengthFields = (unsigned long *)malloc(sizeof(unsigned long) * result->paramCount);
292 memset(result->lengthFields, 0, sizeof(unsigned long) * result->paramCount);
c94bb5aa 293 result->buffers = new Array(result->paramCount, 16, true);
de1d708f 294 *pdwError = DBERR_SUCCESS;
c94bb5aa
VK
295 }
296 else
297 {
de1d708f
VK
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 }
c94bb5aa
VK
307 UpdateErrorMessage(mysql_stmt_error(stmt), errorText);
308 mysql_stmt_close(stmt);
309 }
523f1ceb 310 free(pszQueryUTF8);
c94bb5aa
VK
311 }
312 else
313 {
de1d708f 314 *pdwError = DBERR_OTHER_ERROR;
c94bb5aa
VK
315 UpdateErrorMessage("Call to mysql_stmt_init failed", errorText);
316 }
317 MutexUnlock(pConn->mutexQueryLock);
318 return result;
319}
320
7aad6641
VK
321/**
322 * Bind parameter to prepared statement
323 */
c94bb5aa
VK
324extern "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
fa88627b 328 if ((pos < 1) || (pos > hStmt->paramCount))
c94bb5aa
VK
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);
ef856d02 338 b->buffer_length = (unsigned long)strlen((char *)b->buffer) + 1;
140b8ada
VK
339 hStmt->lengthFields[pos - 1] = b->buffer_length - 1;
340 b->length = &hStmt->lengthFields[pos - 1];
c94bb5aa
VK
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
55285a3e
VK
381/**
382 * Execute prepared statement
383 */
c94bb5aa
VK
384extern "C" DWORD EXPORT DrvExecute(MYSQL_CONN *pConn, MYSQL_STATEMENT *hStmt, WCHAR *errorText)
385{
386 DWORD dwResult;
387
c17f6cbc 388 MutexLock(pConn->mutexQueryLock);
c94bb5aa 389
140b8ada 390 if (mysql_stmt_bind_param(hStmt->statement, hStmt->bindings) == 0)
c94bb5aa
VK
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
7aad6641
VK
420/**
421 * Destroy prepared statement
422 */
c94bb5aa
VK
423extern "C" void EXPORT DrvFreeStatement(MYSQL_STATEMENT *hStmt)
424{
425 if (hStmt == NULL)
426 return;
427
c17f6cbc 428 MutexLock(hStmt->connection->mutexQueryLock);
c94bb5aa 429 mysql_stmt_close(hStmt->statement);
fa88627b 430 MutexUnlock(hStmt->connection->mutexQueryLock);
c94bb5aa
VK
431 delete hStmt->buffers;
432 safe_free(hStmt->bindings);
140b8ada 433 safe_free(hStmt->lengthFields);
c94bb5aa
VK
434 free(hStmt);
435}
436
7aad6641
VK
437/**
438 * Perform actual non-SELECT query
439 */
465b3f2d 440static DWORD DrvQueryInternal(MYSQL_CONN *pConn, const char *pszQuery, WCHAR *errorText)
5039dede
AK
441{
442 DWORD dwRet = DBERR_INVALID_HANDLE;
443
c17f6cbc 444 MutexLock(pConn->mutexQueryLock);
5039dede
AK
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 }
c94bb5aa 462 UpdateErrorMessage(mysql_error(pConn->pMySQL), errorText);
5039dede
AK
463 }
464
465 MutexUnlock(pConn->mutexQueryLock);
466 return dwRet;
467}
468
7aad6641
VK
469/**
470 * Perform non-SELECT query
471 */
465b3f2d 472extern "C" DWORD EXPORT DrvQuery(MYSQL_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText)
5039dede
AK
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
7aad6641
VK
483/**
484 * Perform SELECT query
485 */
465b3f2d 486extern "C" DBDRV_RESULT EXPORT DrvSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
5039dede 487{
c94bb5aa 488 MYSQL_RESULT *result = NULL;
5039dede
AK
489 char *pszQueryUTF8;
490
491 if (pConn == NULL)
492 {
493 *pdwError = DBERR_INVALID_HANDLE;
494 return NULL;
495 }
496
497 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
c17f6cbc 498 MutexLock(pConn->mutexQueryLock);
5039dede
AK
499 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
500 {
c94bb5aa
VK
501 result = (MYSQL_RESULT *)malloc(sizeof(MYSQL_RESULT));
502 result->isPreparedStatement = false;
503 result->resultSet = mysql_store_result(pConn->pMySQL);
5039dede
AK
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 }
c94bb5aa
VK
519 UpdateErrorMessage(mysql_error(pConn->pMySQL), errorText);
520 }
521
522 MutexUnlock(pConn->mutexQueryLock);
523 free(pszQueryUTF8);
524 return result;
525}
526
7aad6641
VK
527/**
528 * Perform SELECT query using prepared statement
529 */
c94bb5aa
VK
530extern "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
c17f6cbc 540 MutexLock(pConn->mutexQueryLock);
c94bb5aa 541
140b8ada 542 if (mysql_stmt_bind_param(hStmt->statement, hStmt->bindings) == 0)
c94bb5aa
VK
543 {
544 if (mysql_stmt_execute(hStmt->statement) == 0)
545 {
546 result = (MYSQL_RESULT *)malloc(sizeof(MYSQL_RESULT));
547 result->isPreparedStatement = true;
fa88627b
VK
548 result->statement = hStmt->statement;
549 result->resultSet = mysql_stmt_result_metadata(hStmt->statement);
c94bb5aa
VK
550 if (result->resultSet != NULL)
551 {
fa88627b
VK
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;
6e3f4556 571 *pdwError = DBERR_SUCCESS;
fa88627b
VK
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 }
c94bb5aa
VK
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
3783d300 593 {
c94bb5aa
VK
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);
3783d300 604 }
5039dede 605 }
c94bb5aa
VK
606 else
607 {
608 UpdateErrorMessage(mysql_stmt_error(hStmt->statement), errorText);
609 *pdwError = DBERR_OTHER_ERROR;
610 }
5039dede
AK
611
612 MutexUnlock(pConn->mutexQueryLock);
c94bb5aa 613 return result;
5039dede
AK
614}
615
7aad6641
VK
616/**
617 * Get field length from result
618 */
c94bb5aa 619extern "C" LONG EXPORT DrvGetFieldLength(MYSQL_RESULT *hResult, int iRow, int iColumn)
5039dede 620{
fa88627b
VK
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);
b1789b80 631 hResult->currentRow = iRow;
fa88627b
VK
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 }
5039dede
AK
641}
642
74d4ba34
VK
643/**
644 * Get field value from result
645 */
c94bb5aa 646extern "C" WCHAR EXPORT *DrvGetField(MYSQL_RESULT *hResult, int iRow, int iColumn, WCHAR *pBuffer, int nBufSize)
5039dede 647{
5039dede
AK
648 WCHAR *pRet = NULL;
649
fa88627b 650 if (hResult->isPreparedStatement)
5039dede 651 {
fa88627b
VK
652 if ((iRow < 0) || (iRow >= hResult->numRows) ||
653 (iColumn < 0) || (iColumn >= hResult->numColumns))
654 return NULL;
655
656 if (hResult->currentRow != iRow)
5039dede 657 {
fa88627b
VK
658 mysql_stmt_data_seek(hResult->statement, iRow);
659 mysql_stmt_fetch(hResult->statement);
b1789b80 660 hResult->currentRow = iRow;
fa88627b
VK
661 }
662
54aa9c6f
VK
663 MYSQL_BIND b;
664 unsigned long l = 0;
665 my_bool isNull;
666
fa88627b
VK
667 memset(&b, 0, sizeof(MYSQL_BIND));
668#if HAVE_ALLOCA || defined(_WIN32)
c453795b 669 b.buffer = alloca(hResult->lengthFields[iColumn] + 1);
fa88627b 670#else
c453795b 671 b.buffer = malloc(hResult->lengthFields[iColumn] + 1);
fa88627b 672#endif
c453795b 673 b.buffer_length = hResult->lengthFields[iColumn] + 1;
fa88627b
VK
674 b.buffer_type = MYSQL_TYPE_STRING;
675 b.length = &l;
676 b.is_null = &isNull;
54aa9c6f
VK
677 int rc = mysql_stmt_fetch_column(hResult->statement, &b, iColumn, 0);
678 if (rc == 0)
fa88627b 679 {
54aa9c6f
VK
680 if (!isNull)
681 {
682 ((char *)b.buffer)[l] = 0;
683 MultiByteToWideChar(CP_UTF8, 0, (char *)b.buffer, -1, pBuffer, nBufSize);
684 pBuffer[nBufSize - 1] = 0;
685 }
686 else
687 {
688 pBuffer[0] = 0;
689 }
5039dede
AK
690 pRet = pBuffer;
691 }
fa88627b
VK
692#if !HAVE_ALLOCA && !defined(_WIN32)
693 free(b.buffer);
694#endif
695 }
696 else
697 {
698 mysql_data_seek(hResult->resultSet, iRow);
699 MYSQL_ROW row = mysql_fetch_row(hResult->resultSet);
700 if (row != NULL)
701 {
702 if (row[iColumn] != NULL)
703 {
704 MultiByteToWideChar(CP_UTF8, 0, row[iColumn], -1, pBuffer, nBufSize);
705 pBuffer[nBufSize - 1] = 0;
706 pRet = pBuffer;
707 }
708 }
5039dede
AK
709 }
710 return pRet;
711}
712
54aa9c6f
VK
713/**
714 * Get number of rows in result
715 */
c94bb5aa 716extern "C" int EXPORT DrvGetNumRows(MYSQL_RESULT *hResult)
5039dede 717{
54aa9c6f 718 return (hResult != NULL) ? (int)(hResult->isPreparedStatement ? hResult->numRows : mysql_num_rows(hResult->resultSet)) : 0;
480c7789
VK
719}
720
54aa9c6f
VK
721/**
722 * Get column count in query result
723 */
c94bb5aa 724extern "C" int EXPORT DrvGetColumnCount(MYSQL_RESULT *hResult)
480c7789 725{
c94bb5aa 726 return (hResult != NULL) ? (int)mysql_num_fields(hResult->resultSet) : 0;
480c7789
VK
727}
728
54aa9c6f
VK
729/**
730 * Get column name in query result
731 */
c94bb5aa 732extern "C" const char EXPORT *DrvGetColumnName(MYSQL_RESULT *hResult, int column)
480c7789 733{
2a8e6a7b
VK
734 MYSQL_FIELD *field;
735
736 if (hResult == NULL)
737 return NULL;
738
c94bb5aa 739 field = mysql_fetch_field_direct(hResult->resultSet, column);
2a8e6a7b 740 return (field != NULL) ? field->name : NULL;
5039dede
AK
741}
742
54aa9c6f
VK
743/**
744 * Free SELECT results
745 */
c94bb5aa 746extern "C" void EXPORT DrvFreeResult(MYSQL_RESULT *hResult)
5039dede 747{
c94bb5aa
VK
748 if (hResult == NULL)
749 return;
750
fa88627b
VK
751 if (hResult->isPreparedStatement)
752 {
753 safe_free(hResult->bindings);
754 safe_free(hResult->lengthFields);
755 }
756
c94bb5aa
VK
757 mysql_free_result(hResult->resultSet);
758 free(hResult);
5039dede
AK
759}
760
54aa9c6f
VK
761/**
762 * Perform asynchronous SELECT query
763 */
764extern "C" DBDRV_ASYNC_RESULT EXPORT DrvAsyncSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
5039dede
AK
765{
766 MYSQL_ASYNC_RESULT *pResult = NULL;
767 char *pszQueryUTF8;
768
769 if (pConn == NULL)
770 {
771 *pdwError = DBERR_INVALID_HANDLE;
772 return NULL;
773 }
774
775 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
c17f6cbc 776 MutexLock(pConn->mutexQueryLock);
5039dede
AK
777 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
778 {
779 pResult = (MYSQL_ASYNC_RESULT *)malloc(sizeof(MYSQL_ASYNC_RESULT));
780 pResult->pConn = pConn;
781 pResult->pHandle = mysql_use_result(pConn->pMySQL);
782 if (pResult->pHandle != NULL)
783 {
784 pResult->bNoMoreRows = FALSE;
785 pResult->iNumCols = mysql_num_fields(pResult->pHandle);
786 pResult->pCurrRow = NULL;
787 pResult->pulColLengths = (unsigned long *)malloc(sizeof(unsigned long) * pResult->iNumCols);
788 }
789 else
790 {
791 free(pResult);
792 pResult = NULL;
793 }
794
795 *pdwError = DBERR_SUCCESS;
796 if (errorText != NULL)
797 *errorText = 0;
798 }
799 else
800 {
801 int nErr = mysql_errno(pConn->pMySQL);
802 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
803 {
804 *pdwError = DBERR_CONNECTION_LOST;
805 }
806 else
807 {
808 *pdwError = DBERR_OTHER_ERROR;
809 }
810
811 if (errorText != NULL)
3783d300 812 {
465b3f2d
VK
813 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mysql_error(pConn->pMySQL), -1, errorText, DBDRV_MAX_ERROR_TEXT);
814 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
815 RemoveTrailingCRLFW(errorText);
3783d300 816 }
5039dede
AK
817 }
818
819 if (pResult == NULL)
820 {
821 MutexUnlock(pConn->mutexQueryLock);
822 }
823 free(pszQueryUTF8);
824
825 return pResult;
826}
827
828
829//
830// Fetch next result line from asynchronous SELECT results
831//
832
fa88627b 833extern "C" BOOL EXPORT DrvFetch(MYSQL_ASYNC_RESULT *hResult)
5039dede
AK
834{
835 BOOL bResult = TRUE;
836
837 if (hResult == NULL)
838 {
839 bResult = FALSE;
840 }
841 else
842 {
843 // Try to fetch next row from server
fa88627b
VK
844 hResult->pCurrRow = mysql_fetch_row(hResult->pHandle);
845 if (hResult->pCurrRow == NULL)
5039dede 846 {
fa88627b 847 hResult->bNoMoreRows = TRUE;
5039dede 848 bResult = FALSE;
fa88627b 849 MutexUnlock(hResult->pConn->mutexQueryLock);
5039dede
AK
850 }
851 else
852 {
853 unsigned long *pLen;
854
855 // Get column lengths for current row
fa88627b 856 pLen = mysql_fetch_lengths(hResult->pHandle);
5039dede
AK
857 if (pLen != NULL)
858 {
fa88627b 859 memcpy(hResult->pulColLengths, pLen, sizeof(unsigned long) * hResult->iNumCols);
5039dede
AK
860 }
861 else
862 {
fa88627b 863 memset(hResult->pulColLengths, 0, sizeof(unsigned long) * hResult->iNumCols);
5039dede
AK
864 }
865 }
866 }
867 return bResult;
868}
869
870
61f032f5
VK
871//
872// Get field length from async query result result
873//
874
fa88627b 875extern "C" LONG EXPORT DrvGetFieldLengthAsync(MYSQL_ASYNC_RESULT *hResult, int iColumn)
61f032f5
VK
876{
877 // Check if we have valid result handle
878 if (hResult == NULL)
879 return 0;
880
881 // Check if there are valid fetched row
fa88627b 882 if (hResult->bNoMoreRows || (hResult->pCurrRow == NULL))
61f032f5
VK
883 return 0;
884
885 // Check if column number is valid
fa88627b 886 if ((iColumn < 0) || (iColumn >= hResult->iNumCols))
61f032f5
VK
887 return 0;
888
fa88627b 889 return hResult->pulColLengths[iColumn];
61f032f5
VK
890}
891
892
5039dede
AK
893//
894// Get field from current row in async query result
895//
896
b8c1ec69 897extern "C" WCHAR EXPORT *DrvGetFieldAsync(DBDRV_ASYNC_RESULT hResult, int iColumn,
5039dede
AK
898 WCHAR *pBuffer, int iBufSize)
899{
900 int iLen;
901
902 // Check if we have valid result handle
903 if (hResult == NULL)
904 return NULL;
905
906 // Check if there are valid fetched row
907 if ((((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows) ||
908 (((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow == NULL))
909 return NULL;
910
911 // Check if column number is valid
912 if ((iColumn < 0) || (iColumn >= ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols))
913 return NULL;
914
915 // Now get column data
916 iLen = min((int)((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths[iColumn], iBufSize - 1);
917 if (iLen > 0)
918 {
919 MultiByteToWideChar(CP_UTF8, 0, ((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow[iColumn],
920 iLen, pBuffer, iBufSize);
921 }
922 pBuffer[iLen] = 0;
923
924 return pBuffer;
925}
926
927
480c7789
VK
928//
929// Get column count in async query result
930//
931
b8c1ec69 932extern "C" int EXPORT DrvGetColumnCountAsync(DBDRV_ASYNC_RESULT hResult)
480c7789
VK
933{
934 return ((hResult != NULL) && (((MYSQL_ASYNC_RESULT *)hResult)->pHandle != NULL))? (int)mysql_num_fields(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) : 0;
935}
936
937
938//
939// Get column name in async query result
940//
941
b8c1ec69 942extern "C" const char EXPORT *DrvGetColumnNameAsync(DBDRV_ASYNC_RESULT hResult, int column)
480c7789 943{
2a8e6a7b
VK
944 MYSQL_FIELD *field;
945
946 if ((hResult == NULL) || (((MYSQL_ASYNC_RESULT *)hResult)->pHandle == NULL))
947 return NULL;
948
949 field = mysql_fetch_field_direct(((MYSQL_ASYNC_RESULT *)hResult)->pHandle, column);
950 return (field != NULL) ? field->name : NULL;
480c7789
VK
951}
952
953
5039dede
AK
954//
955// Destroy result of async query
956//
957
b8c1ec69 958extern "C" void EXPORT DrvFreeAsyncResult(DBDRV_ASYNC_RESULT hResult)
5039dede
AK
959{
960 if (hResult != NULL)
961 {
962 // Check if all result rows fetched
963 if (!((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows)
964 {
965 // Fetch remaining rows
966 while(mysql_fetch_row(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) != NULL);
967
968 // Now we are ready for next query, so unlock query mutex
969 MutexUnlock(((MYSQL_ASYNC_RESULT *)hResult)->pConn->mutexQueryLock);
970 }
971
972 // Free allocated memory
973 mysql_free_result(((MYSQL_ASYNC_RESULT *)hResult)->pHandle);
974 safe_free(((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths);
975 free(hResult);
976 }
977}
978
979
980//
981// Begin transaction
982//
983
984extern "C" DWORD EXPORT DrvBegin(MYSQL_CONN *pConn)
985{
986 return DrvQueryInternal(pConn, "BEGIN", NULL);
987}
988
989
990//
991// Commit transaction
992//
993
994extern "C" DWORD EXPORT DrvCommit(MYSQL_CONN *pConn)
995{
996 return DrvQueryInternal(pConn, "COMMIT", NULL);
997}
998
999
1000//
1001// Rollback transaction
1002//
1003
1004extern "C" DWORD EXPORT DrvRollback(MYSQL_CONN *pConn)
1005{
1006 return DrvQueryInternal(pConn, "ROLLBACK", NULL);
1007}
1008
1009
1010//
1011// DLL Entry point
1012//
1013
1014#ifdef _WIN32
1015
1016BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1017{
1018 if (dwReason == DLL_PROCESS_ATTACH)
1019 DisableThreadLibraryCalls(hInstance);
1020 return TRUE;
1021}
1022
1023#endif