[FIX] SQL_NUMERIC または SQL_C_BINARY データを使用した Jet ODBC Driver のメモリ リーク

文書翻訳 文書翻訳
文書番号: 273772 - 対象製品
この記事は、以前は次の ID で公開されていました: JP273772
すべて展開する | すべて折りたたむ

目次

現象

Jet ODBC ドライバの SQLBindParameter 機能を呼び出すとき、SQL_NUMERIC の SQL タイプを結合するかまたは SQL_C_BINARY を SQL_WCHAR に結合すると、メモリ リークが発生します。

パフォーマンス モニタ (PerfMon) を使用してプライベート バイトの処理を観察していると、メモリは少しずつ着実に増加していき、ステートメントや接続が閉じられてもメモリは解放されません。

原因

バッファは、Jet ODBC ドライバ内部で、ODBC パラメータ データ型を Jet データベース エンジン用のネイティブなデータ型に変換するために使用されます。これらのバッファは、ステートメント ハンドルに保持されているポインタを通して、パラメータ記述子 (IPD) の一部として参照されます。

普通、コードはポインタが有効なメモリ バッファを既に参照しているかをチェックし、バッファが存在すればそれを再利用します。しかし、SQL_NUMERIC データ型を結合しているとき、または SQL_C_BINARY を SQL_WCHAR に結合しているとき、新しいメモリはそれを最初にチェックすることなしにポインタに割り当てられ、前の値は上書きされます。

状況

Windows 2000

この問題を解決するためのモジュールは、Windows 2000 日本語版 Service Pack 2 以降に含まれております。
Windows 2000 日本語版の最新 Service Pack については、以下 Web サイトから入手できます。
http://support.microsoft.com/intl/japan/support/servicepacks/win2ksp.asp?sp=2

詳細

処理を実行している間、処理されているメモリを定期的に検査すると、そのたびに 0x100000 (1048576) のバイト数が割り当てられていくことに気づくはずです。さらに処理が続行されると、その処理によってメモリは最終的に使い果たされ、応答しなくなる (ハングアップする) するか、障害が発生します。

問題の再現手順

  1. Microsoft Visual C++ コンソール アプリケーション内に続くコードをコピーし、その後そのコードをコンパイルします。その際、データソース名、ユーザー ID、およびパスワードを変更しなければならないことにご注意ください。
    #include <windows.h>
    #include <sql.h>
    #include <sqlext.h>
    #include <tchar.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    
    #define LEAK_NUMERIC 1		//Use this to determine NUMERIC or BINARY leak
    
    
    void HandleError(SQLHANDLE	hHandle, SQLSMALLINT hType, RETCODE RetCode)
    {
    	SQLSMALLINT	iRec = 0;
    	SQLINTEGER	iError;
    	TCHAR		szMessage[1000];
    	TCHAR		szState[SQL_SQLSTATE_SIZE];
    
    
    	if (RetCode == SQL_INVALID_HANDLE)
    	{
    		fprintf(stderr,"Invalid handle!\n");
    		return;
    	}
    
    	while (SQLGetDiagRec(hType,
    			 hHandle,
    			 ++iRec,
    			 (SQLCHAR *)szState,
    			 &iError,
    			 (SQLCHAR *)szMessage,
    			 (SQLSMALLINT)(sizeof(szMessage) / sizeof(TCHAR)),
    			 (SQLSMALLINT *)NULL) == SQL_SUCCESS)
    	{
    		fprintf(stderr,TEXT("[%5.5s] %s (%d)\n"),szState,szMessage,iError);
    	}
    
    }
    
    
    char* szConnStringIn = "Driver={Microsoft Access Driver (*.mdb)};DBQ=E:\\JetLeak\\TestDatabase.mdb";
    char* szDropTable = "DROP TABLE LeakTable";
    //char* szInsertStatement = "INSERT INTO LeakTable VALUES (?)";
    char* szSelectStatement = "SELECT * FROM LeakTable WHERE val1 = ?";
    const int nParamCount = 1; 
    
    #if LEAK_NUMERIC
    char* szCreateTable = "CREATE TABLE LeakTable (val1 long)";
    #else
    char* szCreateTable = "CREATE TABLE LeakTable (val1 varchar(10))";
    #endif
    
    
    
    void main(int argc, char* argv[])
    {
    	SQLHENV henv;
    	SQLHDBC hdbc;
    	SQLHSTMT hstmt;
    	SQLRETURN nstatus;
    
    	char szConnStringOut[1024];
    	SQLSMALLINT cbConnOut;
    	SQLINTEGER status[nParamCount];
    
    #if LEAK_NUMERIC
    	SQLCHAR szParam[nParamCount][10] = {"12345"};
    #else
    	BYTE szParam[nParamCount][10] = {0x31,0x33,0x34,0x39};
    #endif
        
    	//Not checking the return codes in some cases for clarity.
    	
    	nstatus = SQLAllocHandle(SQL_HANDLE_ENV,NULL,&henv);
    	nstatus = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,0);
    	nstatus = SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
    
    	nstatus = SQLDriverConnect(hdbc,
    			NULL,
    			(SQLCHAR*) szConnStringIn,
    			SQL_NTS,
    			(SQLCHAR*) szConnStringOut,
    			sizeof (szConnStringOut),
    			&cbConnOut,
    			SQL_DRIVER_COMPLETE);
    							
    	if (nstatus != SQL_SUCCESS && nstatus != SQL_SUCCESS_WITH_INFO)
    	{
    		HandleError(hdbc,SQL_HANDLE_DBC,nstatus);
    		return;
    	}
    
    	nstatus = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);	
    
    	nstatus = SQLExecDirect(hstmt, (SQLCHAR*) szDropTable, SQL_NTS);
    	nstatus = SQLExecDirect(hstmt, (SQLCHAR*) szCreateTable, SQL_NTS);
    
    	if (!SQL_SUCCEEDED(nstatus))
    	{
    		HandleError(hstmt,SQL_HANDLE_STMT,nstatus);
    	}
    
    	int i;
    	//only one parameter in this case
    	for (i=0; i < nParamCount; i++)
    	{
    		status[i] = SQL_NTS;
    
    
    
    #if LEAK_NUMERIC
    		nstatus = SQLBindParameter(hstmt,
    			i+1,
    			SQL_PARAM_INPUT,
    			SQL_C_CHAR,	
    			SQL_NUMERIC, 
    			10, 
    			0,
    			szParam[i],
    			10,
    			&status[i]);
    #else
    
    		nstatus = SQLBindParameter(hstmt,
    			i+1,
    			SQL_PARAM_INPUT,
    			SQL_C_BINARY,	
    			SQL_WCHAR,
    			10, 
    			0,
    			szParam[i],
    			10,
    			&status[i]);
    #endif
    	
    	
    	}
    
    	nstatus = SQLPrepare(hstmt,(SQLCHAR*) szSelectStatement, SQL_NTS);
    	if (nstatus != SQL_SUCCESS)
    	{
    		HandleError(hstmt,SQL_HANDLE_STMT,nstatus);
    	}
    
    	for (i=0; i < 100000; i++)
    	{
    		if (i % 100 == 0)
    		{
    			printf("Selected %d times\n", i);
    			//printf("Inserted %d records\n", i);
    			Sleep(100);
    		}
    		nstatus = SQLExecute(hstmt);
    		if (nstatus != SQL_SUCCESS)
    		{
    			HandleError(hstmt,SQL_HANDLE_STMT,nstatus);
    		}
    
    		SQLFreeStmt(hstmt, SQL_CLOSE);
    	}
    
    	nstatus = SQLExecDirect(hstmt, (SQLCHAR*) "DELETE FROM LeakTable", SQL_NTS);
    	if (nstatus != SQL_SUCCESS)
    	{
    		HandleError(hstmt,SQL_HANDLE_STMT,nstatus);
    	}
    
    	SQLFreeStmt(hstmt, SQL_CLOSE);
    	SQLDisconnect(hdbc);
    
    }
    
    : 定数 LEAK_NUMERIC を使用して、SQL_NUMERIC か SQL_C_BINARY のいずれかのリークを示すことができます。
  2. 接続ストリングの場所によって指定されている、新しい空白の Microsoft Access データベースを作成します。
  3. コードの実行を開始し、Performance Monitor を使用して処理に対するプライベート バイト カウンタを監視します。

    コードが動作している間、プライベート バイト カウンタはたえず増加しています。

関連情報

この資料は米国 Microsoft Corporation から提供されている Knowledge Base の Article ID 273772 (最終更新日 2001-02-22) をもとに作成したものです。

プロパティ

文書番号: 273772 - 最終更新日: 2006年3月23日 - リビジョン: 3.1
この資料は以下の製品について記述したものです。
  • Microsoft ODBC (Open Database Connectivity) Driver for Access 4.0
  • Microsoft Data Access Components 2.5
  • Microsoft Data Access Components 2.5 Service Pack 1
キーワード:?
kbbug kbfix kbjet kbmdac250bug kbgrpdsvcdb kbgrpdsmdac kbmdac250sp1bug kbhotfixserver KB273772
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"

フィードバック

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com