Bug #38396 C API: mysql_stmt_execute crash if called when the server is down
Submitted: 27 Jul 2008 17:49 Modified: 27 Jul 2008 19:18
Reporter: Roman Bobrov Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server Severity:S1 (Critical)
Version:5.0.51b OS:Microsoft Windows (XP SP2)
Assigned to: CPU Architecture:Any
Tags: mysql_stmt_execute crash

[27 Jul 2008 17:49] Roman Bobrov
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".
[27 Jul 2008 19:18] Sveta Smirnova
Please do not submit the same bug more than once. An existing bug report already describes this very problem. Even if you feel that your issue is somewhat different, the resolution is likely
to be the same. Because of this, we hope you add your comments to the original bug instead.

Thank you for your interest in MySQL.

Duplicate of bug #33384