Bug #65136 | my_thread_var not getting initialized | ||
---|---|---|---|
Submitted: | 27 Apr 2012 16:35 | Modified: | 4 May 2012 10:53 |
Reporter: | Steven Cain | Email Updates: | |
Status: | Verified | Impact on me: | |
Category: | Connector / ODBC | Severity: | S3 (Non-critical) |
Version: | 5.5.23 | OS: | Windows |
Assigned to: | Bogdan Degtyariov | CPU Architecture: | Any |
[27 Apr 2012 16:35]
Steven Cain
[30 Apr 2012 9:31]
Bogdan Degtyariov
Which version of Connector/ODBC you are using? 5.5.23 is the version of the server or client library, but not ODBC driver.
[30 Apr 2012 15:09]
Steven Cain
I am using Connector/ODBC version 5.1.10.
[30 Apr 2012 15:10]
Steven Cain
When I compiled the driver I used Connector/ODBC version 5.1.10 and mysql source code version 5.5.23 to compile libmysql.
[1 May 2012 3:49]
Bogdan Degtyariov
Thanks for your reply. Can you send us the output from the following shell command: ldd libmyodbc5.so I want to find out whether it is trying to load libmysqlclient_r.so shared library or the client code was statically linked into the driver binary file.
[1 May 2012 15:10]
Steven Cain
The driver that I am compiling and running is for Windows. I am building the driver in Visual Studio 2010 and replacing the myodbc5.dll on my client machine with the one that I compiled.
[2 May 2012 4:38]
Bogdan Degtyariov
Steven, Thanks for your reply. Please note that my_thread_global_init() and mysql_server_init() are not called explicitly anywhere in Connector/ODBC code. The driver only calls my_init() function upon loading the library, which should be sufficient for initializing all mysql client internals: my_init() initializes some global variables that MySQL needs. It also calls mysql_thread_init() for this thread. It is necessary for my_init() to be called early in the initialization phase of a program's use of the MySQL library. However, my_init() is automatically called by mysql_init(), mysql_library_init(), mysql_server_init(), and mysql_connect(). If you ensure that your program invokes one of those functions before any other MySQL calls, there is no need to invoke my_init() explicitly. http://dev.mysql.com/doc/refman/5.5/en/my-init.html NOTE: this comment from Connector/ODBC source might be essential for understanding how the threading is designed in the driver: /* We don't explicitly call my_thread_init() to avoid initialization in threads that may not even make ODBC calls. my_thread_init() will be called implicitly when mysys calls are made from the thread. */ Also, my_init() cannot be called twice in two different threads. The second call of my_init() can only be possible if myodbc5.dll library is unloaded from memory and then loaded again. This is different from what you have showed in your scheme how to repeat the problem. So, I have two questions: 1. Since you built the driver on your own and we do not support the custom builds, have you tried the release binary version for Windows OS? 2. As I explained above, I do not completely understand how to repeat the problem. Maybe you could provide a simplified C/C++ test case? Thanks.
[2 May 2012 15:34]
Steven Cain
I compiled this program using Visual Studio 6. I get an access violation most of the time that I run it. Let me know how it performs for you. temp.csv contains the numbers 0-9 each on their own line. Here's some test code: // BugTest.cpp : Defines the entry point for the console application. // #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <stdio.h> #include <windows.h> #include <winbase.h> #include <process.h> #include <sqlext.h> int g_bProcess = 1; unsigned __stdcall SQLThread( void * ) { char szConnect[] = "DRIVER=MySQL ODBC 5.1 Driver;SERVER=youserver;TRUSTED_CONNECTION=no;NETWORK=dbmssocn;OPTION=67108864;DATABASE=test;UID=user;PWD=pass;"; char szConnectOut[1024]; // CREATE TABLE `test_load_data` ( // `test_load_data` int(11) NOT NULL AUTO_INCREMENT, // `Message` varchar(32) DEFAULT NULL, // PRIMARY KEY (`test_load_data`) // ) ENGINE=MyISAM AUTO_INCREMENT=113211 DEFAULT CHARSET=latin1 char szSQLStatement[] = "LOAD DATA LOCAL INFILE 'c:\\\\temp\\\\test.csv' INTO TABLE test.test_load_data FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' (Message);"; SQLHENV hEnv = NULL; SQLHDBC hDbc = NULL; SQLHSTMT hStmt = NULL; SQLSMALLINT iConnectOutLengthOut; int iReturnValue; int bReconnect = 0; int bLoadData = 1; int bGetMoreResults; int bFirstLoop = 1; while(g_bProcess) { if(hDbc == NULL) { if(bFirstLoop) { bFirstLoop = 0; } else { Sleep(10000); } printf("Initializing\n"); SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv); SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); printf("Connecting\n"); iReturnValue = SQLDriverConnect(hDbc, NULL, (SQLCHAR*)szConnect, strlen(szConnect), (SQLCHAR*)szConnectOut, 0, &iConnectOutLengthOut, SQL_DRIVER_NOPROMPT); if(iReturnValue != SQL_SUCCESS && iReturnValue != SQL_SUCCESS_WITH_INFO) { printf("Could not connect to %s\n", szConnect); SQLFreeHandle(SQL_HANDLE_DBC, hDbc); hDbc = NULL; SQLFreeHandle(SQL_HANDLE_ENV, hEnv); hEnv = NULL; continue; } } while(bLoadData && g_bProcess) { printf("Allocating statement\n"); SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt); printf("Executing statement\n"); iReturnValue = SQLExecDirect(hStmt, (SQLCHAR*)(szSQLStatement), SQL_NTS); if(iReturnValue == SQL_SUCCESS || iReturnValue == SQL_NO_DATA) { bGetMoreResults = 1; while(bGetMoreResults) { printf("Fetching results\n"); iReturnValue = SQLFetch(hStmt); if(iReturnValue == SQL_SUCCESS || iReturnValue == SQL_SUCCESS_WITH_INFO) { // Store data from the resultset. } printf("Getting more results\n"); iReturnValue = SQLMoreResults(hStmt); if(iReturnValue == SQL_SUCCESS || iReturnValue == SQL_SUCCESS_WITH_INFO) { continue; } else if(iReturnValue == SQL_NO_DATA) { bGetMoreResults = 0; } else { // Error. bGetMoreResults = 0; bLoadData = 0; bReconnect = 1; } } } else { printf("Error executing statement %s\n", szSQLStatement); bLoadData = 0; bReconnect = 1; } printf("Freeing statement\n"); SQLFreeHandle(SQL_HANDLE_STMT, hStmt); hStmt = NULL; Sleep(100); } if(bReconnect || !g_bProcess) { printf("Disconnecting\n"); SQLDisconnect(hDbc); printf("Uninitialization\n"); SQLFreeHandle(SQL_HANDLE_DBC, hDbc); hDbc = NULL; SQLFreeHandle(SQL_HANDLE_ENV, hEnv); hEnv = NULL; } } return 0; } int main(int argc, char* argv[]) { unsigned long hThread; unsigned uThreadId; int iThreadsCreated = 0; printf("Start\n"); // Create some threads that will start using the driver all at once. for(int i = 0; i < 10; i ++) { hThread = _beginthreadex(NULL, 0, SQLThread, NULL, 0, &uThreadId); if(hThread != 0) iThreadsCreated ++; } if(iThreadsCreated) { printf("Let the threads do stuff\n"); Sleep(20000); printf("Tell the threads to stop\n"); g_bProcess = 0; Sleep(15000); } printf("All done\n"); return 0; }
[4 May 2012 7:08]
Bogdan Degtyariov
Thanks for the test case. First of all, we do not recommend using the unrecognized option names for ODBC driver: TRUSTED_CONNECTION=no;NETWORK=dbmssocn; These two options are just ignored. Another thing is the driver name, which should be enclosed in {}: DRIVER={MySQL ODBC 5.1 Driver} Nevertheless, I will try running it and then let you know the results.
[4 May 2012 10:25]
Bogdan Degtyariov
one more thing: after executing LOAD DATA INFILE it is not correct to get results using SQLFetch() because no resultset is returned at all. I inserted the error handling and got the following error message: Error: [MySQL][ODBC 5.1 Driver][mysqld-5.5.15-log]Fetch without a SELECT
[4 May 2012 10:53]
Bogdan Degtyariov
There is no crash if SQLFetch() is not called. This is incorrect to call SQLFetch() when no result set is expected, but the driver should not crash anyway... Need to check this.