2 ** MySQL Database Driver
3 ** Copyright (C) 2003-2015 Victor Kirhenshtein
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.
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.
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.
25 DECLARE_DRIVER_HEADER("MYSQL")
28 * Update error message from given source
30 static void UpdateErrorMessage(const char *source
, WCHAR
*errorText
)
32 if (errorText
!= NULL
)
34 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, source
, -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
35 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
36 RemoveTrailingCRLFW(errorText
);
41 * Update buffer length in DrvPrepareStringW
43 #define UPDATE_LENGTH \
45 if (len >= bufferSize - 1) \
48 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); \
52 * Prepare string for using in SQL query - enclose in quotes and escape as needed
53 * (wide string version)
55 extern "C" WCHAR EXPORT
*DrvPrepareStringW(const WCHAR
*str
)
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
));
62 const WCHAR
*src
= str
;
64 for(outPos
= 1; *src
!= 0; src
++)
69 out
[outPos
++] = L
'\'';
70 out
[outPos
++] = L
'\'';
74 out
[outPos
++] = L
'\\';
75 out
[outPos
++] = L
'\r';
79 out
[outPos
++] = L
'\\';
80 out
[outPos
++] = L
'\n';
84 out
[outPos
++] = L
'\\';
85 out
[outPos
++] = L
'\b';
89 out
[outPos
++] = L
'\\';
90 out
[outPos
++] = L
'\t';
94 out
[outPos
++] = L
'\\';
98 out
[outPos
++] = L
'\\';
99 out
[outPos
++] = L
'\\';
103 out
[outPos
++] = *src
;
107 out
[outPos
++] = L
'\'';
116 * Update buffer length in DrvPrepareStringA
118 #define UPDATE_LENGTH \
120 if (len >= bufferSize - 1) \
123 out = (char *)realloc(out, bufferSize); \
127 * Prepare string for using in SQL query - enclose in quotes and escape as needed
128 * (multibyte string version)
130 extern "C" char EXPORT
*DrvPrepareStringA(const char *str
)
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
);
137 const char *src
= str
;
139 for(outPos
= 1; *src
!= 0; src
++)
144 out
[outPos
++] = '\'';
145 out
[outPos
++] = '\'';
149 out
[outPos
++] = '\\';
150 out
[outPos
++] = '\r';
154 out
[outPos
++] = '\\';
155 out
[outPos
++] = '\n';
159 out
[outPos
++] = '\\';
160 out
[outPos
++] = '\b';
164 out
[outPos
++] = '\\';
165 out
[outPos
++] = '\t';
169 out
[outPos
++] = '\\';
173 out
[outPos
++] = '\\';
174 out
[outPos
++] = '\\';
178 out
[outPos
++] = *src
;
182 out
[outPos
++] = '\'';
193 extern "C" bool EXPORT
DrvInit(const char *cmdLine
)
195 return mysql_library_init(0, NULL
, NULL
) == 0;
201 extern "C" void EXPORT
DrvUnload()
207 * Connect to database
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
)
214 const char *pHost
= szHost
;
215 const char *pSocket
= NULL
;
217 pMySQL
= mysql_init(NULL
);
220 wcscpy(errorText
, L
"Insufficient memory to allocate connection handle");
224 pSocket
= strstr(szHost
, "socket:");
231 if (!mysql_real_connect(
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
242 UpdateErrorMessage(mysql_error(pMySQL
), errorText
);
247 pConn
= (MYSQL_CONN
*)malloc(sizeof(MYSQL_CONN
));
248 pConn
->pMySQL
= pMySQL
;
249 pConn
->mutexQueryLock
= MutexCreate();
251 // Switch to UTF-8 encoding
252 mysql_set_character_set(pMySQL
, "utf8");
254 return (DBDRV_CONNECTION
)pConn
;
258 * Disconnect from database
260 extern "C" void EXPORT
DrvDisconnect(MYSQL_CONN
*pConn
)
264 mysql_close(pConn
->pMySQL
);
265 MutexDestroy(pConn
->mutexQueryLock
);
273 extern "C" DBDRV_STATEMENT EXPORT
DrvPrepare(MYSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
275 MYSQL_STATEMENT
*result
= NULL
;
277 MutexLock(pConn
->mutexQueryLock
);
278 MYSQL_STMT
*stmt
= mysql_stmt_init(pConn
->pMySQL
);
281 char *pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
282 int rc
= mysql_stmt_prepare(stmt
, pszQueryUTF8
, (unsigned long)strlen(pszQueryUTF8
));
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
;
298 int nErr
= mysql_errno(pConn
->pMySQL
);
299 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
)
301 *pdwError
= DBERR_CONNECTION_LOST
;
305 *pdwError
= DBERR_OTHER_ERROR
;
307 UpdateErrorMessage(mysql_stmt_error(stmt
), errorText
);
308 mysql_stmt_close(stmt
);
314 *pdwError
= DBERR_OTHER_ERROR
;
315 UpdateErrorMessage("Call to mysql_stmt_init failed", errorText
);
317 MutexUnlock(pConn
->mutexQueryLock
);
322 * Bind parameter to prepared statement
324 extern "C" void EXPORT
DrvBind(MYSQL_STATEMENT
*hStmt
, int pos
, int sqlType
, int cType
, void *buffer
, int allocType
)
326 static size_t bufferSize
[] = { 0, sizeof(INT32
), sizeof(UINT32
), sizeof(INT64
), sizeof(UINT64
), sizeof(double), 0 };
328 if ((pos
< 1) || (pos
> hStmt
->paramCount
))
330 MYSQL_BIND
*b
= &hStmt
->bindings
[pos
- 1];
332 if (cType
== DB_CTYPE_STRING
)
334 b
->buffer
= UTF8StringFromWideString((WCHAR
*)buffer
);
335 hStmt
->buffers
->add(b
->buffer
);
336 if (allocType
== DB_BIND_DYNAMIC
)
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
;
343 else if (cType
== DB_CTYPE_UTF8_STRING
)
345 b
->buffer
= (allocType
== DB_BIND_DYNAMIC
) ? buffer
: strdup((char *)buffer
);
346 hStmt
->buffers
->add(b
->buffer
);
347 b
->buffer_length
= (unsigned long)strlen((char *)b
->buffer
) + 1;
348 hStmt
->lengthFields
[pos
- 1] = b
->buffer_length
- 1;
349 b
->length
= &hStmt
->lengthFields
[pos
- 1];
350 b
->buffer_type
= MYSQL_TYPE_STRING
;
359 case DB_BIND_DYNAMIC
:
361 hStmt
->buffers
->add(buffer
);
363 case DB_BIND_TRANSIENT
:
364 b
->buffer
= nx_memdup(buffer
, bufferSize
[cType
]);
365 hStmt
->buffers
->add(b
->buffer
);
368 return; // Invalid call
373 case DB_CTYPE_UINT32
:
374 b
->is_unsigned
= true;
377 b
->buffer_type
= MYSQL_TYPE_LONG
;
379 case DB_CTYPE_UINT64
:
380 b
->is_unsigned
= true;
383 b
->buffer_type
= MYSQL_TYPE_LONGLONG
;
385 case DB_CTYPE_DOUBLE
:
386 b
->buffer_type
= MYSQL_TYPE_DOUBLE
;
393 * Execute prepared statement
395 extern "C" DWORD EXPORT
DrvExecute(MYSQL_CONN
*pConn
, MYSQL_STATEMENT
*hStmt
, WCHAR
*errorText
)
399 MutexLock(pConn
->mutexQueryLock
);
401 if (mysql_stmt_bind_param(hStmt
->statement
, hStmt
->bindings
) == 0)
403 if (mysql_stmt_execute(hStmt
->statement
) == 0)
405 dwResult
= DBERR_SUCCESS
;
409 int nErr
= mysql_errno(pConn
->pMySQL
);
410 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
)
412 dwResult
= DBERR_CONNECTION_LOST
;
416 dwResult
= DBERR_OTHER_ERROR
;
418 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
423 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
424 dwResult
= DBERR_OTHER_ERROR
;
427 MutexUnlock(pConn
->mutexQueryLock
);
432 * Destroy prepared statement
434 extern "C" void EXPORT
DrvFreeStatement(MYSQL_STATEMENT
*hStmt
)
439 MutexLock(hStmt
->connection
->mutexQueryLock
);
440 mysql_stmt_close(hStmt
->statement
);
441 MutexUnlock(hStmt
->connection
->mutexQueryLock
);
442 delete hStmt
->buffers
;
443 safe_free(hStmt
->bindings
);
444 safe_free(hStmt
->lengthFields
);
449 * Perform actual non-SELECT query
451 static DWORD
DrvQueryInternal(MYSQL_CONN
*pConn
, const char *pszQuery
, WCHAR
*errorText
)
453 DWORD dwRet
= DBERR_INVALID_HANDLE
;
455 MutexLock(pConn
->mutexQueryLock
);
456 if (mysql_query(pConn
->pMySQL
, pszQuery
) == 0)
458 dwRet
= DBERR_SUCCESS
;
459 if (errorText
!= NULL
)
464 int nErr
= mysql_errno(pConn
->pMySQL
);
465 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
) // CR_SERVER_GONE_ERROR - ???
467 dwRet
= DBERR_CONNECTION_LOST
;
471 dwRet
= DBERR_OTHER_ERROR
;
473 UpdateErrorMessage(mysql_error(pConn
->pMySQL
), errorText
);
476 MutexUnlock(pConn
->mutexQueryLock
);
481 * Perform non-SELECT query
483 extern "C" DWORD EXPORT
DrvQuery(MYSQL_CONN
*pConn
, WCHAR
*pwszQuery
, WCHAR
*errorText
)
488 pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
489 dwRet
= DrvQueryInternal(pConn
, pszQueryUTF8
, errorText
);
495 * Perform SELECT query
497 extern "C" DBDRV_RESULT EXPORT
DrvSelect(MYSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
499 MYSQL_RESULT
*result
= NULL
;
504 *pdwError
= DBERR_INVALID_HANDLE
;
508 pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
509 MutexLock(pConn
->mutexQueryLock
);
510 if (mysql_query(pConn
->pMySQL
, pszQueryUTF8
) == 0)
512 result
= (MYSQL_RESULT
*)malloc(sizeof(MYSQL_RESULT
));
513 result
->connection
= pConn
;
514 result
->isPreparedStatement
= false;
515 result
->resultSet
= mysql_store_result(pConn
->pMySQL
);
516 *pdwError
= DBERR_SUCCESS
;
517 if (errorText
!= NULL
)
522 int nErr
= mysql_errno(pConn
->pMySQL
);
523 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
) // CR_SERVER_GONE_ERROR - ???
525 *pdwError
= DBERR_CONNECTION_LOST
;
529 *pdwError
= DBERR_OTHER_ERROR
;
531 UpdateErrorMessage(mysql_error(pConn
->pMySQL
), errorText
);
534 MutexUnlock(pConn
->mutexQueryLock
);
540 * Perform SELECT query using prepared statement
542 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(MYSQL_CONN
*pConn
, MYSQL_STATEMENT
*hStmt
, DWORD
*pdwError
, WCHAR
*errorText
)
544 MYSQL_RESULT
*result
= NULL
;
548 *pdwError
= DBERR_INVALID_HANDLE
;
552 MutexLock(pConn
->mutexQueryLock
);
554 if (mysql_stmt_bind_param(hStmt
->statement
, hStmt
->bindings
) == 0)
556 if (mysql_stmt_execute(hStmt
->statement
) == 0)
558 result
= (MYSQL_RESULT
*)malloc(sizeof(MYSQL_RESULT
));
559 result
->connection
= pConn
;
560 result
->isPreparedStatement
= true;
561 result
->statement
= hStmt
->statement
;
562 result
->resultSet
= mysql_stmt_result_metadata(hStmt
->statement
);
563 if (result
->resultSet
!= NULL
)
565 result
->numColumns
= mysql_num_fields(result
->resultSet
);
567 result
->lengthFields
= (unsigned long *)malloc(sizeof(unsigned long) * result
->numColumns
);
568 memset(result
->lengthFields
, 0, sizeof(unsigned long) * result
->numColumns
);
570 result
->bindings
= (MYSQL_BIND
*)malloc(sizeof(MYSQL_BIND
) * result
->numColumns
);
571 memset(result
->bindings
, 0, sizeof(MYSQL_BIND
) * result
->numColumns
);
572 for(int i
= 0; i
< result
->numColumns
; i
++)
574 result
->bindings
[i
].buffer_type
= MYSQL_TYPE_STRING
;
575 result
->bindings
[i
].length
= &result
->lengthFields
[i
];
578 mysql_stmt_bind_result(hStmt
->statement
, result
->bindings
);
580 if (mysql_stmt_store_result(hStmt
->statement
) == 0)
582 result
->numRows
= (int)mysql_stmt_num_rows(hStmt
->statement
);
583 result
->currentRow
= -1;
584 *pdwError
= DBERR_SUCCESS
;
588 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
589 *pdwError
= DBERR_OTHER_ERROR
;
590 mysql_free_result(result
->resultSet
);
591 free(result
->bindings
);
592 free(result
->lengthFields
);
599 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
600 *pdwError
= DBERR_OTHER_ERROR
;
607 int nErr
= mysql_errno(pConn
->pMySQL
);
608 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
)
610 *pdwError
= DBERR_CONNECTION_LOST
;
614 *pdwError
= DBERR_OTHER_ERROR
;
616 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
621 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
622 *pdwError
= DBERR_OTHER_ERROR
;
625 MutexUnlock(pConn
->mutexQueryLock
);
630 * Get field length from result
632 extern "C" LONG EXPORT
DrvGetFieldLength(MYSQL_RESULT
*hResult
, int iRow
, int iColumn
)
634 if (hResult
->isPreparedStatement
)
636 if ((iRow
< 0) || (iRow
>= hResult
->numRows
) ||
637 (iColumn
< 0) || (iColumn
>= hResult
->numColumns
))
640 if (hResult
->currentRow
!= iRow
)
642 MutexLock(hResult
->connection
->mutexQueryLock
);
643 mysql_stmt_data_seek(hResult
->statement
, iRow
);
644 mysql_stmt_fetch(hResult
->statement
);
645 hResult
->currentRow
= iRow
;
646 MutexUnlock(hResult
->connection
->mutexQueryLock
);
648 return (LONG
)hResult
->lengthFields
[iColumn
];
652 mysql_data_seek(hResult
->resultSet
, iRow
);
653 MYSQL_ROW row
= mysql_fetch_row(hResult
->resultSet
);
654 return (row
== NULL
) ? (LONG
)-1 : ((row
[iColumn
] == NULL
) ? -1 : (LONG
)strlen(row
[iColumn
]));
659 * Get field value from result - UNICODE and UTF8 implementation
661 static void *GetFieldInternal(MYSQL_RESULT
*hResult
, int iRow
, int iColumn
, void *pBuffer
, int nBufSize
, bool utf8
)
665 if (hResult
->isPreparedStatement
)
667 if ((iRow
< 0) || (iRow
>= hResult
->numRows
) ||
668 (iColumn
< 0) || (iColumn
>= hResult
->numColumns
))
671 MutexLock(hResult
->connection
->mutexQueryLock
);
672 if (hResult
->currentRow
!= iRow
)
674 mysql_stmt_data_seek(hResult
->statement
, iRow
);
675 mysql_stmt_fetch(hResult
->statement
);
676 hResult
->currentRow
= iRow
;
683 memset(&b
, 0, sizeof(MYSQL_BIND
));
685 b
.buffer
= alloca(hResult
->lengthFields
[iColumn
] + 1);
687 b
.buffer
= malloc(hResult
->lengthFields
[iColumn
] + 1);
689 b
.buffer_length
= hResult
->lengthFields
[iColumn
] + 1;
690 b
.buffer_type
= MYSQL_TYPE_STRING
;
693 int rc
= mysql_stmt_fetch_column(hResult
->statement
, &b
, iColumn
, 0);
698 ((char *)b
.buffer
)[l
] = 0;
701 strncpy((char *)pBuffer
, (char *)b
.buffer
, nBufSize
);
702 ((char *)pBuffer
)[nBufSize
- 1] = 0;
706 MultiByteToWideChar(CP_UTF8
, 0, (char *)b
.buffer
, -1, (WCHAR
*)pBuffer
, nBufSize
);
707 ((WCHAR
*)pBuffer
)[nBufSize
- 1] = 0;
713 *((char *)pBuffer
) = 0;
715 *((WCHAR
*)pBuffer
) = 0;
719 MutexUnlock(hResult
->connection
->mutexQueryLock
);
726 mysql_data_seek(hResult
->resultSet
, iRow
);
727 MYSQL_ROW row
= mysql_fetch_row(hResult
->resultSet
);
730 if (row
[iColumn
] != NULL
)
734 strncpy((char *)pBuffer
, row
[iColumn
], nBufSize
);
735 ((char *)pBuffer
)[nBufSize
- 1] = 0;
739 MultiByteToWideChar(CP_UTF8
, 0, row
[iColumn
], -1, (WCHAR
*)pBuffer
, nBufSize
);
740 ((WCHAR
*)pBuffer
)[nBufSize
- 1] = 0;
750 * Get field value from result
752 extern "C" WCHAR EXPORT
*DrvGetField(MYSQL_RESULT
*hResult
, int iRow
, int iColumn
, WCHAR
*pBuffer
, int nBufSize
)
754 return (WCHAR
*)GetFieldInternal(hResult
, iRow
, iColumn
, pBuffer
, nBufSize
, false);
758 * Get field value from result as UTF8 string
760 extern "C" char EXPORT
*DrvGetFieldUTF8(MYSQL_RESULT
*hResult
, int iRow
, int iColumn
, char *pBuffer
, int nBufSize
)
762 return (char *)GetFieldInternal(hResult
, iRow
, iColumn
, pBuffer
, nBufSize
, true);
766 * Get number of rows in result
768 extern "C" int EXPORT
DrvGetNumRows(MYSQL_RESULT
*hResult
)
770 return (hResult
!= NULL
) ? (int)(hResult
->isPreparedStatement
? hResult
->numRows
: mysql_num_rows(hResult
->resultSet
)) : 0;
774 * Get column count in query result
776 extern "C" int EXPORT
DrvGetColumnCount(MYSQL_RESULT
*hResult
)
778 return (hResult
!= NULL
) ? (int)mysql_num_fields(hResult
->resultSet
) : 0;
782 * Get column name in query result
784 extern "C" const char EXPORT
*DrvGetColumnName(MYSQL_RESULT
*hResult
, int column
)
791 field
= mysql_fetch_field_direct(hResult
->resultSet
, column
);
792 return (field
!= NULL
) ? field
->name
: NULL
;
796 * Free SELECT results
798 extern "C" void EXPORT
DrvFreeResult(MYSQL_RESULT
*hResult
)
803 if (hResult
->isPreparedStatement
)
805 safe_free(hResult
->bindings
);
806 safe_free(hResult
->lengthFields
);
809 mysql_free_result(hResult
->resultSet
);
814 * Perform unbuffered SELECT query
816 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(MYSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
818 MYSQL_UNBUFFERED_RESULT
*pResult
= NULL
;
823 *pdwError
= DBERR_INVALID_HANDLE
;
827 pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
828 MutexLock(pConn
->mutexQueryLock
);
829 if (mysql_query(pConn
->pMySQL
, pszQueryUTF8
) == 0)
831 pResult
= (MYSQL_UNBUFFERED_RESULT
*)malloc(sizeof(MYSQL_UNBUFFERED_RESULT
));
832 pResult
->connection
= pConn
;
833 pResult
->isPreparedStatement
= false;
834 pResult
->resultSet
= mysql_use_result(pConn
->pMySQL
);
835 if (pResult
->resultSet
!= NULL
)
837 pResult
->noMoreRows
= false;
838 pResult
->numColumns
= mysql_num_fields(pResult
->resultSet
);
839 pResult
->pCurrRow
= NULL
;
840 pResult
->lengthFields
= (unsigned long *)malloc(sizeof(unsigned long) * pResult
->numColumns
);
841 pResult
->bindings
= NULL
;
849 *pdwError
= DBERR_SUCCESS
;
850 if (errorText
!= NULL
)
855 int nErr
= mysql_errno(pConn
->pMySQL
);
856 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
) // CR_SERVER_GONE_ERROR - ???
858 *pdwError
= DBERR_CONNECTION_LOST
;
862 *pdwError
= DBERR_OTHER_ERROR
;
865 if (errorText
!= NULL
)
867 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, mysql_error(pConn
->pMySQL
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
868 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
869 RemoveTrailingCRLFW(errorText
);
875 MutexUnlock(pConn
->mutexQueryLock
);
883 * Perform unbuffered SELECT query using prepared statement
885 extern "C" DBDRV_RESULT EXPORT
DrvSelectPreparedUnbuffered(MYSQL_CONN
*pConn
, MYSQL_STATEMENT
*hStmt
, DWORD
*pdwError
, WCHAR
*errorText
)
887 MYSQL_UNBUFFERED_RESULT
*result
= NULL
;
889 MutexLock(pConn
->mutexQueryLock
);
891 if (mysql_stmt_bind_param(hStmt
->statement
, hStmt
->bindings
) == 0)
893 if (mysql_stmt_execute(hStmt
->statement
) == 0)
895 result
= (MYSQL_UNBUFFERED_RESULT
*)malloc(sizeof(MYSQL_UNBUFFERED_RESULT
));
896 result
->connection
= pConn
;
897 result
->isPreparedStatement
= true;
898 result
->statement
= hStmt
->statement
;
899 result
->resultSet
= mysql_stmt_result_metadata(hStmt
->statement
);
900 if (result
->resultSet
!= NULL
)
902 result
->noMoreRows
= false;
903 result
->numColumns
= mysql_num_fields(result
->resultSet
);
904 result
->pCurrRow
= NULL
;
906 result
->lengthFields
= (unsigned long *)malloc(sizeof(unsigned long) * result
->numColumns
);
907 memset(result
->lengthFields
, 0, sizeof(unsigned long) * result
->numColumns
);
909 result
->bindings
= (MYSQL_BIND
*)malloc(sizeof(MYSQL_BIND
) * result
->numColumns
);
910 memset(result
->bindings
, 0, sizeof(MYSQL_BIND
) * result
->numColumns
);
911 for(int i
= 0; i
< result
->numColumns
; i
++)
913 result
->bindings
[i
].buffer_type
= MYSQL_TYPE_STRING
;
914 result
->bindings
[i
].length
= &result
->lengthFields
[i
];
917 mysql_stmt_bind_result(hStmt
->statement
, result
->bindings
);
918 *pdwError
= DBERR_SUCCESS
;
922 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
923 *pdwError
= DBERR_OTHER_ERROR
;
930 int nErr
= mysql_errno(pConn
->pMySQL
);
931 if (nErr
== CR_SERVER_LOST
|| nErr
== CR_CONNECTION_ERROR
|| nErr
== CR_SERVER_GONE_ERROR
)
933 *pdwError
= DBERR_CONNECTION_LOST
;
937 *pdwError
= DBERR_OTHER_ERROR
;
939 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
944 UpdateErrorMessage(mysql_stmt_error(hStmt
->statement
), errorText
);
945 *pdwError
= DBERR_OTHER_ERROR
;
950 MutexUnlock(pConn
->mutexQueryLock
);
956 * Fetch next result line from asynchronous SELECT results
958 extern "C" bool EXPORT
DrvFetch(MYSQL_UNBUFFERED_RESULT
*result
)
960 if ((result
== NULL
) || (result
->noMoreRows
))
965 if (result
->isPreparedStatement
)
967 int rc
= mysql_stmt_fetch(result
->statement
);
968 if ((rc
!= 0) && (rc
!= MYSQL_DATA_TRUNCATED
))
970 result
->noMoreRows
= true;
972 MutexUnlock(result
->connection
->mutexQueryLock
);
977 // Try to fetch next row from server
978 result
->pCurrRow
= mysql_fetch_row(result
->resultSet
);
979 if (result
->pCurrRow
== NULL
)
981 result
->noMoreRows
= true;
983 MutexUnlock(result
->connection
->mutexQueryLock
);
989 // Get column lengths for current row
990 pLen
= mysql_fetch_lengths(result
->resultSet
);
993 memcpy(result
->lengthFields
, pLen
, sizeof(unsigned long) * result
->numColumns
);
997 memset(result
->lengthFields
, 0, sizeof(unsigned long) * result
->numColumns
);
1005 * Get field length from async query result result
1007 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(MYSQL_UNBUFFERED_RESULT
*hResult
, int iColumn
)
1009 // Check if we have valid result handle
1010 if (hResult
== NULL
)
1013 // Check if there are valid fetched row
1014 if (hResult
->noMoreRows
|| ((hResult
->pCurrRow
== NULL
) && !hResult
->isPreparedStatement
))
1017 // Check if column number is valid
1018 if ((iColumn
< 0) || (iColumn
>= hResult
->numColumns
))
1021 return hResult
->lengthFields
[iColumn
];
1025 * Get field from current row in async query result
1027 extern "C" WCHAR EXPORT
*DrvGetFieldUnbuffered(MYSQL_UNBUFFERED_RESULT
*hResult
, int iColumn
, WCHAR
*pBuffer
, int iBufSize
)
1029 // Check if we have valid result handle
1030 if (hResult
== NULL
)
1033 // Check if there are valid fetched row
1034 if ((hResult
->noMoreRows
) || ((hResult
->pCurrRow
== NULL
) && !hResult
->isPreparedStatement
))
1037 // Check if column number is valid
1038 if ((iColumn
< 0) || (iColumn
>= hResult
->numColumns
))
1041 // Now get column data
1042 WCHAR
*value
= NULL
;
1043 if (hResult
->isPreparedStatement
)
1046 unsigned long l
= 0;
1049 memset(&b
, 0, sizeof(MYSQL_BIND
));
1051 b
.buffer
= alloca(hResult
->lengthFields
[iColumn
] + 1);
1053 b
.buffer
= malloc(hResult
->lengthFields
[iColumn
] + 1);
1055 b
.buffer_length
= hResult
->lengthFields
[iColumn
] + 1;
1056 b
.buffer_type
= MYSQL_TYPE_STRING
;
1058 b
.is_null
= &isNull
;
1059 int rc
= mysql_stmt_fetch_column(hResult
->statement
, &b
, iColumn
, 0);
1064 ((char *)b
.buffer
)[l
] = 0;
1065 MultiByteToWideChar(CP_UTF8
, 0, (char *)b
.buffer
, -1, (WCHAR
*)pBuffer
, iBufSize
);
1066 ((WCHAR
*)pBuffer
)[iBufSize
- 1] = 0;
1070 *((WCHAR
*)pBuffer
) = 0;
1080 int iLen
= min((int)hResult
->lengthFields
[iColumn
], iBufSize
- 1);
1083 MultiByteToWideChar(CP_UTF8
, 0, hResult
->pCurrRow
[iColumn
], iLen
, pBuffer
, iBufSize
);
1092 * Get column count in async query result
1094 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(MYSQL_UNBUFFERED_RESULT
*hResult
)
1096 return (hResult
!= NULL
) ? hResult
->numColumns
: 0;
1100 * Get column name in async query result
1102 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(MYSQL_UNBUFFERED_RESULT
*hResult
, int column
)
1106 if ((hResult
== NULL
) || (hResult
->resultSet
== NULL
))
1109 field
= mysql_fetch_field_direct(hResult
->resultSet
, column
);
1110 return (field
!= NULL
) ? field
->name
: NULL
;
1114 * Destroy result of async query
1116 extern "C" void EXPORT
DrvFreeUnbufferedResult(MYSQL_UNBUFFERED_RESULT
*hResult
)
1118 if (hResult
== NULL
)
1121 // Check if all result rows fetched
1122 if (!hResult
->noMoreRows
)
1124 // Fetch remaining rows
1125 if (!hResult
->isPreparedStatement
)
1127 while(mysql_fetch_row(hResult
->resultSet
) != NULL
);
1130 // Now we are ready for next query, so unlock query mutex
1131 MutexUnlock(hResult
->connection
->mutexQueryLock
);
1134 // Free allocated memory
1135 mysql_free_result(hResult
->resultSet
);
1136 free(hResult
->lengthFields
);
1137 free(hResult
->bindings
);
1144 extern "C" DWORD EXPORT
DrvBegin(MYSQL_CONN
*pConn
)
1146 return DrvQueryInternal(pConn
, "BEGIN", NULL
);
1150 * Commit transaction
1152 extern "C" DWORD EXPORT
DrvCommit(MYSQL_CONN
*pConn
)
1154 return DrvQueryInternal(pConn
, "COMMIT", NULL
);
1158 * Rollback transaction
1160 extern "C" DWORD EXPORT
DrvRollback(MYSQL_CONN
*pConn
)
1162 return DrvQueryInternal(pConn
, "ROLLBACK", NULL
);
1166 * Check if table exist
1168 extern "C" int EXPORT
DrvIsTableExist(MYSQL_CONN
*pConn
, const WCHAR
*name
)
1170 WCHAR query
[256], lname
[256];
1171 wcsncpy(lname
, name
, 256);
1173 swprintf(query
, 256, L
"SHOW TABLES LIKE '%ls'", lname
);
1175 WCHAR errorText
[DBDRV_MAX_ERROR_TEXT
];
1176 int rc
= DBIsTableExist_Failure
;
1177 MYSQL_RESULT
*hResult
= (MYSQL_RESULT
*)DrvSelect(pConn
, query
, &error
, errorText
);
1178 if (hResult
!= NULL
)
1180 rc
= (DrvGetNumRows(hResult
) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1181 DrvFreeResult(hResult
);
1191 bool WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1193 if (dwReason
== DLL_PROCESS_ATTACH
)
1194 DisableThreadLibraryCalls(hInstance
);