Cambio de comportamiento del controlador ODBC al administrar las conversiones de caracteres
El controlador ODBC de SQL Server 2012 Native Client (SQLNCLI11.dll) cambió la forma de realizar las conversiones SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) y SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Las funciones ODBC, como SQLGetData, SQLBindCol y SQLBindParameter, devuelven (-4) SQL_NO_TOTAL como el parámetro indicador de longitud al utilizar el controlador ODBC de SQL Server 2012 Native Client. Las versiones anteriores del controlador ODBC de SQL Server Native Client devolvían un valor de longitud, que puede ser incorrecto.
Comportamiento de SQLGetData
Muchas funciones de Windows permiten especificar un tamaño de búfer de 0, siendo la longitud devuelta el tamaño de los datos devueltos. El patrón siguiente es frecuente para los programadores de 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
Sin embargo, SQLGetData no se debe utilizar en este escenario. No se debe utilizar el patrón siguiente:
// 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
Solo se puede llamar a SQLGetData para recuperar fragmentos de datos reales. No se admite el uso de SQLGetData para obtener el tamaño de los datos.
A continuación se muestra el impacto del cambio de controlador cuando se utiliza el patrón incorrecto. Esta aplicación realiza una consulta en una columna varchar y el enlace se especifica como Unicode (SQL_UNICODE/SQL_WCHAR):
Consulta: select convert(varchar(36), '123')
SQLGetData(hstmt, SQL_WCHAR, ….., (SQLPOINTER*) 0x1, 0 , &iSize); // Attempting to determine storage size needed
Versión del controlador ODBC de SQL Server Native Client |
Resultado de indicador o de longitud |
Descripción |
---|---|---|
SQL Server 2008 R2 Native Client o anterior |
6 |
El controlador suponía incorrectamente que la conversión de CHAR en WCHAR se podía conseguir como longitud * 2. |
SQL Server 2012 Native Client (versión 11.0.2100.60) o posterior |
-4 (SQL_NO_TOTAL) |
El controlador ya no da por supuesto que la conversión de CHAR en WCHAR o de WCHAR en CHAR es una acción de (multiplicar) *2 o de (dividir) /2. La llamada a SQLGetData ya no devuelve la longitud de la conversión esperada. El controlador detecta la conversión a o desde CHAR y WCHAR y devuelve (-4) SQL_NO_TOTAL en lugar del comportamiento *2 o /2, que podría ser incorrecto. |
Utilice SQLGetData para recuperar los fragmentos de datos. (Se muestra el pseudocódigo:)
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
}
}
}
Comportamiento de SQLBindCol
Consulta: select convert(varchar(36), '1234567890')
SQLBindCol(… SQL_W_CHAR, …) // Only bound a buffer of WCHAR[4] – Expecting String Data Right Truncation behavior
Versión del controlador ODBC de SQL Server Native Client |
Resultado de indicador o de longitud |
Descripción |
---|---|---|
SQL Server 2008 R2 Native Client o anterior |
20 |
|
SQL Server 2012 Native Client (versión 11.0.2100.60) o posterior |
-4 (SQL_NO_TOTAL) |
|
SQLBindParameter (comportamiento del parámetro OUTPUT)
Consulta: create procedure spTest @p1 varchar(max) OUTPUT
select @p1 = replicate('B', 1234)
SQLBindParameter(… SQL_W_CHAR, …) // Only bind up to first 64 characters
Versión del controlador ODBC de SQL Server Native Client |
Resultado de indicador o de longitud |
Descripción |
---|---|---|
SQL Server 2008 R2 Native Client o anterior |
2468 |
|
SQL Server 2012 Native Client (versión 11.0.2100.60) o posterior |
-4 (SQL_NO_TOTAL) |
|
Realizar conversiones CHAR y WCHAR
El controlador ODBC de SQL Server 2012 Native Client ofrece varias maneras de realizar las conversiones CHAR y WCHAR. La lógica es similar a la manipulación de blobs (varchar(max), nvarchar(max), …):
Los datos se guardan o se truncan en el búfer especificado al enlazar con SQLBindCol o SQLBindParameter.
Si no realiza el enlace, puede recuperar los datos en fragmentos mediante SQLGetData y SQLParamData.