Changement de comportement du pilote ODBC lors de la gestion des conversions de caractères
Le pilote ODBC SQL Server 2012 Native Client (SQLNCLI11.dll) a changé la manière dont il effectue les conversions SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) et SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Les fonctions ODBC, telles que SQLGetData, SQLBindCol et SQLBindParameter, retournent (-4) SQL_NO_TOTAL comme paramètre de longueur/indicateur lors de l'utilisation du pilote ODBC SQL Server 2012 Native Client. Les versions antérieures du pilote ODBC SQL Server Native Client retournaient une valeur de longueur, ce qui peut s'avérer incorrect.
Comportement de SQLGetData
La plupart des fonctions Windows vous permettent de spécifier une taille de mémoire tampon de 0, et la longueur retournée correspond à la taille des données renvoyées. Le motif suivant est bien connu des programmeurs Windows :
int iSize = 0;
BYTE * pBuffer = NULL;
GetMyFavoriteAPI(pBuffer, &iSize); // Returns needed size in iSize
pBuffer = new BYTE[iSize]; // Allocate buffer
GetMyFavoriteAPI(pBuffer, &iSize); // Retrieve actual data
Toutefois, SQLGetData ne doit pas être utilisé dans ce scénario. Le motif suivant ne doit pas être utilisé :
// bad
int iSize = 0;
WCHAR * pBuffer = NULL;
SQLGetData(hstmt, SQL_W_CHAR, ...., (SQLPOINTER*)0x1, 0, &iSize); // Get storage size needed
pBuffer = new WCHAR[(iSize/sizeof(WCHAR)) + 1]; // Allocate buffer
SQLGetData(hstmt, SQL_W_CHAR, ...., (SQLPOINTER*)pBuffer, iSize, &iSize); // Retrieve data
SQLGetData ne peut être appelé que pour récupérer des segments de données réelles. L'utilisation de SQLGetData pour obtenir la taille des données n'est pas prise en charge.
Ce qui suit montre l'impact du changement de pilote lors de l'utilisation d'un motif incorrect. Cette application interroge une colonne varchar et une liaison au format Unicode (SQL_UNICODE/SQL_WCHAR) :
Requête : select convert(varchar(36), '123')
SQLGetData(hstmt, SQL_WCHAR, ….., (SQLPOINTER*) 0x1, 0 , &iSize); // Attempting to determine storage size needed
Version du pilote ODBC SQL Server Native Client |
Résultat de longueur ou d'indicateur |
Description |
---|---|---|
SQL Server 2008 R2 Native Client ou antérieur |
6 |
Le pilote a déduit par erreur que la conversion de CHAR en WCHAR serait obtenue en multipliant la longueur par 2. |
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieur |
-4 (SQL_NO_TOTAL) |
Le pilote ne part plus du principe que la conversion de CHAR en WCHAR ou de WCHAR en CHAR correspond à une opération de multiplication (*2) ou de division (/2). L'appel de SQLGetData ne retourne plus la longueur de la conversion attendue. Le pilote détecte la conversion vers ou depuis CHAR et WCHAR, puis retourne (-4) SQL_NO_TOTAL au lieu du comportement *2 ou /2 qui peut être incorrect. |
Utilisez SQLGetData pour récupérer les segments de données. (Pseudo-code illustré :)
while( (SQL_SUCCESS or SQL_SUCCESS_WITH_INFO) == SQLFetch(...) ) {
SQLNumCols(...iTotalCols...)
for(int iCol = 1; iCol < iTotalCols; iCol++) {
WCHAR* pBufOrig, pBuffer = new WCHAR[100];
SQLGetData(.... iCol … pBuffer, 100, &iSize); // Get original chunk
while(NOT ALL DATA RETREIVED (SQL_NO_TOTAL, ...) ) {
pBuffer += 50; // Advance buffer for data retrieved
// May need to realloc the buffer when you reach current size
SQLGetData(.... iCol … pBuffer, 100, &iSize); // Get next chunk
}
}
}
Comportement de SQLBindCol
Requête : select convert(varchar(36), '1234567890')
SQLBindCol(… SQL_W_CHAR, …) // Only bound a buffer of WCHAR[4] – Expecting String Data Right Truncation behavior
Version du pilote ODBC SQL Server Native Client |
Résultat de longueur ou d'indicateur |
Description |
---|---|---|
SQL Server 2008 R2 Native Client ou antérieur |
20 |
|
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieur |
-4 (SQL_NO_TOTAL) |
|
SQLBindParameter (comportement du paramètre OUTPUT)
Requête : create procedure spTest @p1 varchar(max) OUTPUT
select @p1 = replicate('B', 1234)
SQLBindParameter(… SQL_W_CHAR, …) // Only bind up to first 64 characters
Version du pilote ODBC SQL Server Native Client |
Résultat de longueur ou d'indicateur |
Description |
---|---|---|
SQL Server 2008 R2 Native Client ou antérieur |
2468 |
|
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieur |
-4 (SQL_NO_TOTAL) |
|
Réalisation de conversions CHAR et WCHAR
Le pilote ODBC SQL Server 2012 Native Client propose plusieurs manières d'effectuer des conversions CHAR et WCHAR. La logique est similaire à la manipulation d'objets blob (varchar(max), nvarchar(max), …) :
Les données sont enregistrées ou tronquées dans la mémoire tampon spécifiée lors de la liaison avec SQLBindCol ou SQLBindParameter.
Si vous n'effectuez aucune liaison, vous pouvez récupérer les données dans des segments à l'aide de SQLGetData et de SQLParamData.