Cambio de comportamiento del controlador ODBC al administrar las conversiones de caracteres
Se aplica a: SQL Server Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW)
Importante
SQL Server Native Client (SNAC) no se incluye con:
- SQL Server 2022 (16.x) y versiones posteriores
- SQL Server Management Studio 19 y versiones posteriores
No se recomiendan SQL Server Native Client (SQLNCLI o SQLNCLI11) ni Microsoft OLE DB Provider for SQL Server (SQLOLEDB) heredado para el desarrollo de nuevas aplicaciones.
En el caso de los proyectos nuevos, use uno de los siguientes controladores:
Para SQLNCLI que se incluye como componente de motor de base de datos de SQL Server (versiones 2012 a 2019), consulte esta excepción de ciclo de vida de soporte técnico.
El controlador ODBC de SQL Server 2012 (11.x) Native Client (SQLNCLI11.dll) cambió cómo funciona SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR/NVARCHAR(MAX)) y SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Las funciones ODBC, como SQLGetData, SQLBindCol, SQLBindParameter, devuelven (-4) SQL_NO_TOTAL como parámetro length/indicador al usar el controlador ODBC de SQL Server 2012 Native Client. Las versiones anteriores del controlador ODBC de SQL Server Native Client devolvieron 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 debe usarse 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 consulta una columna varchar y un enlace 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 (10.50.x) Native Client o versiones anteriores | 6 | El controlador suponía incorrectamente que la conversión de CHAR en WCHAR se podía conseguir como longitud * 2. |
SQL Server 2012 (11.x) Native Client (versión 11.0.2100.60) o posterior | -4 (SQL_NO_TOTAL) | El controlador ya no supone que la conversión de CHAR a WCHAR o WCHAR a CHAR es una acción (multiplicar) *2 o (dividir)/2. Llamar 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. |
Use SQLGetData para recuperar los fragmentos de los 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 (10.50.x) Native Client o versiones anteriores | 20 | SQLFetch informa de que hay un truncamiento en el lado derecho de los datos. La longitud es la longitud de los datos devueltos, no lo que se almacenó (se supone una conversión *2 de CHAR en WCHAR, lo que puede ser incorrecto para los glifos). Los datos almacenados en el búfer son 123\0. Se garantiza que el búfer está terminado en NULL. |
SQL Server 2012 (11.x) Native Client (versión 11.0.2100.60) o posterior | -4 (SQL_NO_TOTAL) | SQLFetch informa de que hay un truncamiento en el lado derecho de los datos. La longitud indica -4 (SQL_NO_TOTAL) debido a que el resto de los datos no se convirtió. Los datos almacenados en el búfer son 123\0. - Se garantiza que el búfer está terminado en NULL. |
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 (10.50.x) Native Client o versiones anteriores | 2468 | SQLFetch no devuelve más datos disponibles. SQLMoreResults no devuelve más datos disponibles. La longitud indica el tamaño de los datos devueltos desde el servidor, no el de los datos almacenados en el búfer. El búfer original contiene 63 bytes y un terminador NULL. Se garantiza que el búfer está terminado en NULL. |
SQL Server 2012 (11.x) Native Client (versión 11.0.2100.60) o posterior | -4 (SQL_NO_TOTAL) | SQLFetch no devuelve más datos disponibles. SQLMoreResults no devuelve más datos disponibles. La longitud indica (-4) SQL_NO_TOTAL debido a que el resto de los datos no se convirtió. El búfer original contiene 63 bytes y un terminador NULL. Se garantiza que el búfer está terminado en NULL. |
Realizar conversiones CHAR y WCHAR
El controlador ODBC de SQL Server 2012 (11.x) Native Client ofrece varias maneras de realizar conversiones CHAR y WCHAR. La lógica es similar a manipular blobs (varchar(max), nvarchar(max), ...):
Los datos se guardan o truncan en el búfer especificado al enlazar con SQLBindCol o SQLBindParameter.
Si no se enlaza, puede recuperar los datos en fragmentos mediante SQLGetData y SQLParamData.