FIX: SQL Server ODBC Driver reuses incorrect parameter length (280294)
The information in this article applies to:
- Microsoft Data Access Components 2.6
- Microsoft ODBC Driver for SQL Server 2000.80.194
- Microsoft SQL Server 2000 Enterprise Edition
- Microsoft SQL Server 2000 Personal Edition
- Microsoft SQL Server 2000 Standard Edition
- Microsoft SQL Server 2000 Workgroup Edition
- Microsoft SQL Server 2000 Developer Edition
This article was previously published under Q280294 SYMPTOMS
Under some circumstances, the SQL Server 2000/MDAC 2.6 RTM version of Microsoft ODBC Driver for SQL Server (2000.80.194) reuses a parameter binding that is too short. This truncates the parameter value and causes incorrect or no data to be returned.
This behavior occurs only when you are using the SQL Server 2000/MDAC 2.6 ODBC driver against a SQL Server 2000 database; it does not occur when you are connecting to a SQL Server 7.0 server or with earlier versions of the driver.
CAUSE
When a statement is prepared and run by the ODBC driver, a query plan for the statement is cached on the server and a handle for the plan is returned to the driver. The information in this query plan is based partly on the size of the parameters that are being used in the statement.
If the size of the bound parameters change, logic in the driver checks to see if the query plan that was cached for the previous running of the statement can still be used. If the query plan cannot be reused because of increased parameter sizes, the plan is unprepared and then re-prepared with larger parameter sizes.
The driver is incorrectly determining that the older query plan (with the shorter parameter length) can be reused. This causes the parameter values to be truncated, which causes the incorrect results.
RESOLUTIONMDAC 2.6
To resolve this problem, obtain the latest service pack for Microsoft Data Access Components 2.6. For more information, click the following article number to view the article in the Microsoft Knowledge Base:
300635 How to obtain the latest MDAC 2.6 service pack
SQL Server 2000
To resolve this problem, obtain the latest service pack for Microsoft SQL Server 2000. For more information, click the following article number to view the article in the Microsoft Knowledge Base:
290211 How to obtain the latest SQL Server 2000 service pack
HotfixNOTE: The following hotfix was created prior to Microsoft SQL Server 2000 Service Pack 1.
The English version of this fix should have the following file attributes or later:
Date Version Size File name
------------------------------------------------------------
10/13/2000 2000.80.212.0 471,119 bytes Sqlsrv32.dll
10/13/2000 2000.80.212.0 90,112 bytes Sqlsrv32.rll
10/13/2000 2000.80.212.0 28,742 bytes Odbcbcp.dll
Please note that the hotfix contains three files. Due to version dependencies, all three files must be installed together.
WORKAROUND
Do one of the following to work around the problem:
- If possible, use an earlier version of the SQL Server ODBC driver, such as the MDAC 2.5 Service Pack 1 (SP1) version (3.70.0820).
- When the parameter is bound the first time, give it the largest size necessary, up to the maximum size of the column.
STATUSMicrosoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section.MDAC
This problem was corrected in Microsoft Data Access Components (MDAC) 2.6 Service Pack 1 and MDAC 2.7.
SQL Server
This problem was corrected in SQL Server 2000 Service Pack 1.
MORE INFORMATIONSteps to Reproduce Behavior- Copy the code below into a new Microsoft Visual C++ console application, and then compile the code. Please note that you may need to change the data source name, database user, and password, so that these values refer to valid values for your SQL Server.
NOTE: You can use the SMALLER_FIRST constant to determine which parameter is bound first.
- With the SMALLER_FIRST constant set to 1 (TRUE), the smaller parameter is bound first, and the second call to SQLExecute returns no data. With the SMALLER_FIRST constant set to 0 (zero), the larger parameter is bound first, and data is returned for both queries.
Sample Code
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
#define SMALLER_FIRST 1 //Use this to determine which parameter is bound first.
//Set to 1 to reproduce the failure.
void main(void)
{
SQLHANDLE henv;
SQLHANDLE hdbc;
SQLHANDLE hstmt;
RETCODE rc;
char Statement[50] = {"select title_id from pubs..titles where title = ?"};
char dsn[10] = {"DSNName"};
char user[5] = {"DatabaseUser"};
char pass[5] = {"Password"};
char m_titleid[10] = {"\0"};
char param2[22] = {"The Gourmet Microwave"};
char param1[14] = {"Net Etiquette"};
param1[13] = '\0';
param2[21] = '\0';
long sqlnts = SQL_NTS;
long m_bytesRet = SQL_NTS;
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
rc = SQLConnect(hdbc, (SQLCHAR *)dsn, SQL_NTS, (SQLCHAR *) user, SQL_NTS,
(SQLCHAR *) pass, SQL_NTS);
rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
rc = SQLPrepare(hstmt, (SQLCHAR *)Statement, SQL_NTS);
#if SMALLER_FIRST //Put smaller parameter first.
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WCHAR, 13, 0,
(SQLCHAR*)param1, 13, &sqlnts);
#else
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WCHAR, 21, 0,
(SQLCHAR*)param2, 21, &sqlnts);
#endif
rc = SQLExecute(hstmt);
rc = SQLFetch(hstmt);
if (rc == SQL_NO_DATA_FOUND)
{
printf("Parameter #1 (%s) returned no data.\n", param1);
SQLFreeStmt(hstmt, SQL_DROP);
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
exit(0);
}
else
{
SQLGetData(hstmt, 1, SQL_C_CHAR, m_titleid, 10, &m_bytesRet);
printf("Title_id for parameter (%s) = %s\n", SMALLER_FIRST?param1:param2, m_titleid);
}
rc = SQLFreeStmt(hstmt, SQL_CLOSE);
memset(m_titleid,0,10);
#if SMALLER_FIRST //Put larger parameter last.
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WCHAR, 21, 0,
(SQLCHAR*)param2, 21, &sqlnts);
#else
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WCHAR, 13, 0,
(SQLCHAR*)param1, 13, &sqlnts);
#endif
rc = SQLExecute(hstmt);
rc = SQLFetch(hstmt);
if (rc == SQL_NO_DATA_FOUND)
{
printf("Parameter #2 (%s) returned no data.\n", param2);
SQLFreeStmt(hstmt, SQL_DROP);
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
exit(0);
}
else
{
SQLGetData(hstmt, 1, SQL_C_CHAR, m_titleid, 10, &m_bytesRet);
printf("Title_id for parameter (%s) = %s\n", SMALLER_FIRST?param2:param1, m_titleid);
}
rc = SQLFreeStmt(hstmt, SQL_CLOSE);
rc = SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
Modification Type: | Major | Last Reviewed: | 7/27/2006 |
---|
Keywords: | kbHotfixServer kbBug kbfix kbMDAC260sp1Fix kbQFE kbSQLServ2000sp1fix KB280294 kbAudDeveloper kbAudITPRO |
---|
|