Compartilhar via


Alteração do comportamento do driver ODBC ao lidar com conversões de caracteres

O Driver ODBC do SQL Server 2012 Native Client (SQLNCLI11.dll) alterou o funcionamento das conversões SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) e SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). 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, o que pode estar incorreto.

Comportamento do SQLGetData

Muitas funções do Windows permitem especificar um tamanho de 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 neste cenário. O seguinte padrão não deve ser usado:

// 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. O uso do 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. Este aplicativo consulta uma coluna e uma varchar associação 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 do SQL Server Native Client Comprimento ou resultado do indicador Descrição
SQL Server 2008 R2 Native Client ou anterior 6 O driver assumiu incorretamente que a conversão de CHAR em WCHAR poderia ser realizada como comprimento * 2.
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior -4 (SQL_NO_TOTAL) O driver não pressupõe mais que converter de CHAR em WCHAR ou WCHAR em CHAR é uma ação (multiplicar) *2 ou (dividir)/2.

Chamar SQLGetData não retorna mais o comprimento da conversão esperada. O driver detecta a conversão de ou para CHAR e WCHAR e retorna (-4) SQL_NO_TOTAL em vez de *2 ou /2 comportamento que pode 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 do SQL Server Native Client Comprimento ou resultado do indicador Descrição
SQL Server 2008 R2 Native Client ou anterior 20 - O SQLFetch informa que há um truncamento no lado direito dos dados.
- O comprimento é o comprimento dos dados retornados, não o que foi armazenado (pressupõe *2 CHAR para conversão WCHAR que pode estar incorreto para glifos).
- Os dados armazenados no buffer são 123\0. O buffer tem a garantia de ser encerrado em NULL.
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior -4 (SQL_NO_TOTAL) - O SQLFetch informa que há um truncamento no lado direito dos dados.
- O comprimento indica -4 (SQL_NO_TOTAL) porque o restante dos dados não foi convertido.
- Os dados armazenados no buffer são 123\0. - O buffer tem a garantia de ser encerrado NULL.

SQLBindParameter (comportamento do 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 do SQL Server Native Client Comprimento ou resultado do indicador Descrição
SQL Server 2008 R2 Native Client ou anterior 2468 - O SQLFetch não retorna mais dados disponíveis.
- O SQLMoreResults não retorna mais dados disponíveis.
- O tamanho indica o tamanho dos dados retornados do servidor, não armazenados no buffer.
- O buffer original contém 63 bytes e um terminador NULL. O buffer tem a garantia de ser encerrado em NULL.
SQL Server 2012 Native Client (versão 11.0.2100.60) ou posterior -4 (SQL_NO_TOTAL) - O SQLFetch não retorna mais dados disponíveis.
- O SQLMoreResults não retorna mais dados disponíveis.
- O comprimento indica (-4) SQL_NO_TOTAL porque o restante dos dados não foi convertido.
- O buffer original contém 63 bytes e um terminador NULL. O buffer tem a garantia de ser encerrado em NULL.

Executando 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 à manipulação de blobs (varchar(max), nvarchar(max), ...):

  • Os dados são salvos ou truncados no buffer especificado ao associar com SQLBindCol ou SQLBindParameter.

  • Se você não associar, poderá recuperar os dados em partes usando SQLGetData e SQLParamData.

Consulte Também

Recursos do SQL Server Native Client