Alteração de comportamento do driver ODBC ao lidar com conversões de caracteres
O Driver ODBC do SQL Server 2012 Native Client (SQLNCLI11.dll) alterou a maneira como faz as conversões de SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) e SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). As funções ODBC, como SQLGetData, SQLBindCol, SQLBindParameter, retornam (-4) SQL_NO_TOTAL como o parâmetro de comprimento/indicador ao usar o driver ODBC do SQL Server 2012 Native Client. As versões anteriores do driver ODBC do SQL Server Native Client retornaram um valor de comprimento, que pode estar incorreto.
Comportamento de SQLGetData
Muitas funções do Windows permitem especificar um tamanho do buffer de 0 e o comprimento retornado é o tamanho dos dados retornados. O seguinte padrão é comum para programadores do 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
No entanto, SQLGetData não deve ser usado nesse cenário. Os padrões a seguir não devem ser usados:
// 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 só pode ser chamado para recuperar partes de dados reais. Usar SQLGetData para obter o tamanho dos dados não tem suporte.
O exemplo a seguir mostra o impacto da alteração do driver quando você usa o padrão incorreto. Esse aplicativo consulta uma coluna e uma associação varchar 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
Versão do driver ODBC SQL Server Native Client |
Comprimento ou resultado do indicador |
Descrição |
---|---|---|
SQL Server 2008 R2 Native Client ou anterior |
6 |
O driver presumiu incorretamente que a conversão de CHAR para WCHAR poderia ser realizada como o comprimento * 2. |
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior |
-4 (SQL_NO_TOTAL) |
O driver não presume mais que a conversão de CHAR para WCHAR ou WCHAR para CHAR seja uma ação de *2 (multiplicação) ou /2 (divisão). A chamada a SQLGetData não retorna mais o comprimento da conversão esperada. O driver detecta a conversão para ou de CHAR e WCHAR e retorna (- 4) SQL_NO_TOTAL em vez do comportamento de *2 ou /2 que poderia estar incorreto. |
Use SQLGetData para recuperar as partes dos dados. (Pseudocódigo mostrado:)
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
}
}
}
Comportamento 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
Versão do driver ODBC SQL Server Native Client |
Comprimento ou resultado do indicador |
Descrição |
---|---|---|
SQL Server 2008 R2 Native Client ou anterior |
20 |
|
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior |
-4 (SQL_NO_TOTAL) |
|
SQLBindParameter (comportamento de 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
Versão do driver ODBC SQL Server Native Client |
Comprimento ou resultado do indicador |
Descrição |
---|---|---|
SQL Server 2008 R2 Native Client ou anterior |
2468 |
|
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior |
-4 (SQL_NO_TOTAL) |
|
Execução de conversões CHAR e WCHAR
O driver ODBC do SQL Server 2012 Native Client oferece várias maneiras de executar conversões CHAR e WCHAR. A lógica é semelhante a manipular blobs (varchar(max), nvarchar(max), …):
Os dados são salvos ou truncados no buffer especificado ao associar a SQLBindCol ou SQLBindParameter.
Se você não for associar, poderá recuperar os dados em partes usando SQLGetData e SQLParamData.