| Bug #93593 | ODBC memory leak | ||
|---|---|---|---|
| Submitted: | 13 Dec 2018 14:36 | Modified: | 1 Jun 2020 3:58 |
| Reporter: | Bhargava Srinarasi | Email Updates: | |
| Status: | No Feedback | Impact on me: | |
| Category: | Connector / ODBC | Severity: | S2 (Serious) |
| Version: | 8.0.13 | OS: | Windows |
| Assigned to: | CPU Architecture: | Any | |
| Tags: | memory leak | ||
[1 May 2020 3:58]
MySQL Verification Team
Please try version 8.0.20. Thanks.
[14 May 2020 16:56]
Leigh Anderson
I still have this issue in 8.0.20. If I use 5.3.14 there is no leak.
[2 Jun 2020 1:00]
Bugs System
No feedback was provided for this bug for over a month, so it is being suspended automatically. If you are able to provide the information that was originally requested, please do so and change the status of the bug back to "Open".
[6 Jun 2020 10:08]
Leigh Anderson
I still have this issue with the 8.0.20 driver. If I use the 5.3.14 driver there is no leak.
[29 Mar 2022 12:08]
c t
still existing in mysql-connector-odbc-noinstall-8.0.28-win32
this bug is happening during load/unload of the driver dll.
when you use debugger in VS2017 you can see relation
between load/alloc and unload/NO free.
VS2017 code used for verification and analysis:
// NOTE: Use Multi-Byte Character Set
// tested with latest ODBC driver
// see output for unload/load
// 'memleak_test.exe' (Win32) : Unloaded 'C:\mysql-connector-odbc-noinstall-8.0.28-win32\lib\myodbc8w.dll'
// 'memleak_test.exe' (Win32) : Loaded 'C:\mysql-connector-odbc-noinstall-8.0.28-win32\lib\myodbc8w.dll'.Symbols loaded.
// REDUCED_OUTPUT can be used to prevent non error output of results of various odbc functions
#define REDUCED_OUTPUT 0
// ODBC_DATASOURCE is the ODBC connection used, configured to MySQL ODBC Unicode driver
#define ODBC_DATASOURCE "test_mycustomer"
#include <iostream>
#include <windows.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <psapi.h>
#pragma comment(lib, "odbc32.lib")
class MemleakTest
{
public:
MemleakTest(const std::string &ds)
: odbcDataSource(ds)
{}
~MemleakTest()
{
disconnect();
}
bool connect()
{
const auto envAllocResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
#if !REDUCED_OUTPUT
std::cout << "envAllocResult: " << envAllocResult << std::endl;
#endif
if (SQL_SUCCEEDED(envAllocResult))
{
const auto setEnvAttrResult = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
#if !REDUCED_OUTPUT
std::cout << "setEnvAttrResult: " << setEnvAttrResult << std::endl;
#endif
if (SQL_SUCCEEDED(envAllocResult))
{
const auto hdbcAllocResult = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
#if !REDUCED_OUTPUT
std::cout << "hdbcAllocResult: " << hdbcAllocResult << std::endl;
#endif
if (SQL_SUCCEEDED(hdbcAllocResult))
{
const auto setConnAttrResult = SQLSetConnectAttr(hDbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
#if !REDUCED_OUTPUT
std::cout << "setConnAttrResult: " << setConnAttrResult << std::endl;
#endif
if (SQL_SUCCEEDED(setConnAttrResult))
{
// Connect to data source //
const auto connectResult = SQLConnect(hDbc, (SQLCHAR*)odbcDataSource.c_str(), SQL_NTS, (SQLCHAR*) "", SQL_NTS, (SQLCHAR*) "", SQL_NTS);
#if !REDUCED_OUTPUT
std::cout << "connectResult: " << connectResult << std::endl;
#endif
if (SQL_SUCCEEDED(connectResult))
{
#if !REDUCED_OUTPUT
std::cout << "CONNECTED" << std::endl;
#endif
return true;
}
else
{
std::cerr << "SQLConnect failed: " << connectResult << std::endl;
}
}
else
{
std::cerr << "SQLSetConnectAttr failed: " << setConnAttrResult << std::endl;
}
FreeHandle(SQL_HANDLE_DBC, hDbc);
}
else
{
std::cerr << "SQLAllocHandle hDbc failed: " << hdbcAllocResult << std::endl;
}
}
else
{
std::cerr << "SQLSetEnvAttr failed: " << setEnvAttrResult << std::endl;
}
FreeHandle(SQL_HANDLE_ENV, hEnv);
}
else
{
std::cerr << "SQLAllocHandle HENV failed: " << envAllocResult << std::endl;
}
return false;
}
void disconnect()
{
if (hDbc)
{
const auto disconnectResult = SQLDisconnect(hDbc);
#if !REDUCED_OUTPUT
std::cout << "disconnectResult: " << disconnectResult << std::endl;
#endif
if (!SQL_SUCCEEDED(disconnectResult))
{
std::cerr << "SQLDisconnect failed: " << disconnectResult << std::endl;
}
FreeHandle(SQL_HANDLE_DBC, hDbc);
}
if (hEnv)
{
FreeHandle(SQL_HANDLE_ENV, hEnv);
}
#if !REDUCED_OUTPUT
std::cout << "DISCONNECTED" << std::endl;
#endif
}
private:
std::string odbcDataSource;
SQLHANDLE hEnv = nullptr;
SQLHANDLE hDbc = nullptr;
bool FreeHandle(SQLSMALLINT type, SQLHANDLE &handle)
{
if (nullptr == handle)
{
std::cerr << "invalid handle given" << std::endl;
return false;
}
const auto freeHandleResult = SQLFreeHandle(type, handle);
if (!SQL_SUCCEEDED(freeHandleResult))
{
std::cerr << "SQLFreeHandle(type " << type << ", handle " << handle << ") failed: " << freeHandleResult << std::endl;
return false;
}
handle = nullptr;
return true;
}
};
void printMemory()
{
const HANDLE hProcess = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
{
printf("\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize);
}
CloseHandle(hProcess);
}
int main()
{
std::cout << "***START ";
printMemory();
for (uint32_t counter = 0; counter < 500; ++counter)
{
MemleakTest memleakTest(ODBC_DATASOURCE);
memleakTest.connect();
memleakTest.disconnect();
std::cout << "#" << counter;
printMemory();
Sleep(500); // dont let firewall/server think its DDoS attack
}
std::cout << "***END ";
printMemory();
return 0;
}
[30 Mar 2022 15:32]
c t
Add.: used system: Edition Windows 10 Enterprise Version 20H2 OS build 19042.1586 Experience Windows Feature Experience Pack 120.2212.4170.0

Description: I'm seeing a memory leak in SQLConnect() on Windows when I call SQLConnect() and SQLDisconnect() repeatedly. I can see that the memory that's allocated in SQLConnect() never gets released. A couple of observations that may be interesting keeping the C program below in mind. 1) After the first iteration, hdbc is the same on all iterations! 2) Instead of doing connect and disconnect in the same loop, if I put the handles in an array and disconnect/free them later, I see that there's much less leak. Also hdbc is different in each iteration. How to repeat: Here's a very simple C program which leaks around 150kB per iteration. int main() { for (int i = 0; i < 10; ++i) { SQLHENV henv; SQLHDBC hdbc; SQLRETURN retcode; // Allocate environment handle retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); // Set the ODBC version environment attribute if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); // Allocate connection handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); // Set login timeout to 5 seconds if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); // Connect to data source retcode = SQLConnect(hdbc, (SQLCHAR*) "MySQL", SQL_NTS, (SQLCHAR*) "test", 0, (SQLCHAR*) "test", 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLDisconnect(hdbc); } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv); } }