Description:
Calling mysql_stmt_execute may generate an Access Violation when called after the MySQL server has been shutdown or terminated.
The prepared statement is a INSERT into a table.
MySQL server and client library version 5.0.51b.
DLL version of the client library
Dev Env: Microsoft Visual Studio 6.0.
The complete project (Microsoft Visual Studio 6.0) to reproduce it is uploaded to FTP as bug-data-20087.zip
Here is the listing of main function:
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/)
{
lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
_Module.Init(NULL, hInstance);
PCSTR pcszError = NULL;
MYSQL* pInitConn = ::mysql_init(NULL);
if (pInitConn != NULL)
{
// connect to localhost using anonymous account
// InnoDB is used by default
my_bool fTrue = true;
::mysql_options(pInitConn,MYSQL_OPT_RECONNECT,(PCSTR)&fTrue);
DWORD dwProtocol = MYSQL_PROTOCOL_TCP;
::mysql_options(pInitConn,MYSQL_OPT_PROTOCOL,(PCSTR)&dwProtocol);
::mysql_options(pInitConn,MYSQL_SET_CHARSET_NAME,(PCSTR)"cp1251");
PCSTR pcszURL = "127.0.0.1";
//PCSTR pcszURL = "192.168.0.104";
PCSTR pcszLogin = NULL;
PCSTR pcszPassword = NULL;
MYSQL* pDBConn = ::mysql_real_connect(pInitConn,pcszURL,pcszLogin,pcszPassword,NULL,3306,NULL,0);
if (pDBConn != NULL)
{
DWORD dwVersion = ::mysql_get_server_version(pDBConn);
// create and connect to MyCrashTest db
int nResult = ::mysql_query(pDBConn,"CREATE DATABASE IF NOT EXISTS MyCrashTest");
nResult = ::mysql_query(pDBConn,"USE MyCrashTest");
if (nResult == 0)
{
// create InsertTest table
nResult = ::mysql_query(pDBConn,"CREATE TABLE InsertTest(`ID` INT AUTO_INCREMENT PRIMARY KEY, `Value` INT UNSIGNED DEFAULT \"0\")");
// create prepared statement to insert data into InsertTest table
MYSQL_STMT* pStmt = ::mysql_stmt_init(pDBConn);
if (pStmt != NULL)
{
DWORD dwFields = 1;
MYSQL_BIND* pBind = new MYSQL_BIND[dwFields];
::ZeroMemory(pBind,sizeof(MYSQL_BIND) * dwFields);
DWORD dwLength = 4;
my_bool fIsNull = false;
DWORD dwValue = 0;
pBind[0].length = &dwLength;
pBind[0].is_null = &fIsNull;
pBind[0].is_unsigned = TRUE;
pBind[0].buffer = &dwValue;
pBind[0].buffer_length = 4;
pBind[0].buffer_type = MYSQL_TYPE_LONG;
PCSTR pcszQuery = "INSERT INTO InsertTest (`Value`) VALUES (?)";
int nPrepareRet = ::mysql_stmt_prepare(pStmt,pcszQuery,::lstrlen(pcszQuery));
if (nPrepareRet == 0)
{
if (::mysql_stmt_param_count(pStmt) == dwFields)
{
my_bool fBinded = ::mysql_stmt_bind_param(pStmt,pBind);
if (fBinded == 0)
{
// OK, now insert 3 values...
// server is still working
dwValue = ::GetTickCount();
int nExecuteRet = ::mysql_stmt_execute(pStmt);
// must be ok
// NOW STOP THE MYSQL PROCESS (KILL SERVER)
// server is stopped
::Sleep(500);
dwValue = ::GetTickCount();
nExecuteRet = ::mysql_stmt_execute(pStmt);
// must be error (Can't connect to MySQL server)
::Sleep(500);
dwValue = ::GetTickCount();
nExecuteRet = ::mysql_stmt_execute(pStmt);
// must be Access Violation (0xC0000005)
}
}
}
::mysql_stmt_close(pStmt);
delete[] pBind;
}
}
}
::mysql_close(pInitConn);
pInitConn = NULL;
}
_Module.Term();
CoUninitialize();
return 0;
}
How to repeat:
Have the server up and running and have the client app connect to the server and prepare an INSERT statement.
The connection is set with the OPT_RECONNECT.
While the app is executing the prepared statement (in a loop), shutdown the server.
The statement execution during fails in a controlled manner, the second or third execution of mysql_stmt_execute triggers an access violation exception.
Similar outcomes if the server is killed: the difference is that the first execution reports a failure condition of "Lost connection..." instead of "shutdown in progress".