Partager via


Modification du 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 façon dont il effectue les conversions de SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) et SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Fonctions ODBC, telles que SQLGetData, SQLBindCol, SQLBindParameter, return (-4) SQL_NO_TOTAL comme paramètre length/indicateur lors de l’utilisation du pilote ODBC SQL Server 2012 Native Client. Les versions antérieures du pilote ODBC SQL Server Native Client ont retourné une valeur de longueur, ce qui peut être incorrect.

Comportement de SQLGetData

De nombreuses fonctions Windows vous permettent de spécifier une taille de mémoire tampon de 0 et la longueur renvoyée est la taille des données retournées. Le modèle suivant est courant pour les 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 modèle 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 peut uniquement être appelé pour récupérer des blocs de données réelles. L’utilisation de SQLGetData pour obtenir la taille des données n’est pas prise en charge.

L’exemple suivant montre l’impact du changement de pilote lorsque vous utilisez le modèle incorrect. Cette application interroge une colonne et une varchar liaison en tant qu’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 Longueur ou résultat de l’indicateur Descriptif
SQL Server 2008 R2 Native Client ou version antérieure 6 Le pilote a supposé incorrectement que la conversion de CHAR en WCHAR pouvait être effectuée comme longueur * 2.
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieure -4 (SQL_NO_TOTAL) Le pilote ne suppose plus que la conversion de CHAR en WCHAR ou WCHAR en CHAR est une action *2 ou (diviser)/2.

L’appel de SQLGetData ne retourne plus la longueur de la conversion attendue. Le pilote détecte la conversion vers ou à partir de CHAR et WCHAR et retourne (-4) SQL_NO_TOTAL au lieu du comportement *2 ou /2 qui peut être incorrect.

Utilisez SQLGetData pour récupérer les blocs des 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 Longueur ou résultat de l’indicateur Descriptif
SQL Server 2008 R2 Native Client ou version antérieure 20 - SQLFetch signale qu’il existe une troncation sur le côté droit des données.
- La longueur correspond à la longueur des données retournées, et non à ce qui a été stocké (suppose la conversion *2 CHAR en WCHAR qui peut être incorrecte pour les glyphes).
- Les données stockées dans la mémoire tampon sont 123\0. La mémoire tampon est garantie d’être terminée par NULL.
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieure -4 (SQL_NO_TOTAL) - SQLFetch signale qu’il existe une troncation sur le côté droit des données.
- La longueur indique -4 (SQL_NO_TOTAL), car le reste des données n’a pas été converti.
- Les données stockées dans la mémoire tampon sont 123\0. - La mémoire tampon est garantie d’être terminée par NULL.

SQLBindParameter (comportement des paramètres 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 Longueur ou résultat de l’indicateur Descriptif
SQL Server 2008 R2 Native Client ou version antérieure 2468 - SQLFetch ne retourne plus de données disponibles.
- SQLMoreResults ne retourne plus de données disponibles.
- La longueur indique la taille des données retournées par le serveur, non stockées dans la mémoire tampon.
- La mémoire tampon d’origine contient 63 octets et un terminateur NULL. La mémoire tampon est garantie d’être terminée par NULL.
SQL Server 2012 Native Client (version 11.0.2100.60) ou ultérieure -4 (SQL_NO_TOTAL) - SQLFetch ne retourne plus de données disponibles.
- SQLMoreResults ne retourne plus de données disponibles.
- La longueur indique (-4) SQL_NO_TOTAL car le reste des données n’a pas été converti.
- La mémoire tampon d’origine contient 63 octets et un terminateur NULL. La mémoire tampon est garantie d’être terminée par NULL.

Exécution de conversions CHAR et WCHAR

Le pilote ODBC SQL Server 2012 Native Client offre plusieurs façons 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 ne liez pas, vous pouvez récupérer les données en blocs à l’aide de SQLGetData et DE SQLParamData.

Voir aussi

Fonctionnalités de SQL Server Native Client