Modifica del comportamento del driver ODBC quando si gestiscono le conversioni di caratteri

Si applica a:SQL ServerDatabase SQL diAzure Istanzagestita di SQL di Azure Azure Synapse Analytics PlatformSystem (PDW)

Importante

SQL Server Native Client (spesso abbreviato SNAC) è stato rimosso da SQL Server 2022 (16.x) e SQL Server Management Studio 19 (SSMS). SQL Server Native Client (SQLNCLI o SQLNCLI11) e il provider Microsoft OLE DB legacy per SQL Server (SQLOLEDB) non sono consigliati per lo sviluppo di nuove applicazioni. Passare al nuovo Microsoft OLE DB Driver (MSOLEDBSQL) per SQL Server o alla versione più recente di Microsoft ODBC Driver per SQL Server . Per SQLNCLI fornito come componente del motore di database di SQL Server (versioni da 2012 a 2019), vedere questa eccezione relativa al ciclo di vita del supporto.

Il driver ODBC di SQL Server 2012 (11.x) Native Client (SQLNCLI11.dll) ha modificato le modalità di SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) e SQL_CHAR* (conversioni CHAR/VARCHAR/NARCHAR(MAX)). Le funzioni ODBC, ad esempio SQLGetData, SQLBindCol, SQLBindParameter, restituiscono (-4) SQL_NO_TOTAL come parametro length/indicator quando si usa il driver ODBC di SQL Server 2012 Native Client. Le versioni precedenti del driver ODBC di SQL Server Native Client hanno restituito un valore di lunghezza, che può non essere corretto.

Comportamento di SQLGetData

Tramite le numerose funzioni di Windows è possibile specificare dimensioni del buffer pari a 0 e la lunghezza restituita corrisponde alle dimensioni dei dati restituiti. Di seguito è riportato un modello comune per i programmatori di 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  

Tuttavia, SQLGetData non deve essere usato in questo scenario. Non è consigliabile utilizzare il modello seguente:

// 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 può essere chiamato solo per recuperare blocchi di dati effettivi. L'uso di SQLGetData per ottenere le dimensioni dei dati non è supportato.

Di seguito viene illustrato l'impatto della modifica del driver quando si utilizza il modello errato. Questa applicazione esegue una query su una colonna varchar e un'associazione come Unicode (SQL_UNICODE/SQL_WCHAR):

Query: select convert(varchar(36), '123')

SQLGetData(hstmt, SQL_WCHAR, ....., (SQLPOINTER*) 0x1, 0 , &iSize);   // Attempting to determine storage size needed  
Versione del driver ODBC di SQL Server Native Client Risultato della lunghezza o dell'indicatore Descrizione
SQL Server 2008 R2 (10.50.x) Native Client o versioni precedenti 6 È stato erroneamente presupposto dal driver che la conversione di CHAR in WCHAR potesse essere effettuata come lunghezza * 2.
SQL Server 2012 (11.x) Native Client (versione 11.0.2100.60) o successiva -4 (SQL_NO_TOTAL) Il driver non presuppone più che la conversione da CHAR a WCHAR o WCHAR in CHAR sia un'azione (moltiplica) *2 o (divisione)/2.

La chiamata a SQLGetData non restituisce più la lunghezza della conversione prevista. Tramite il driver viene rilevata la conversione a o da CHAR e WCHAR e viene restituito (-4) SQL_NO_TOTAL anziché il comportamento *2 o /2 che potrebbe essere errato.

Usare SQLGetData per recuperare i blocchi dei dati. (di seguito è riportato uno pseudocodice):

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 di SQLBindCol

Query: select convert(varchar(36), '1234567890')

SQLBindCol(... SQL_W_CHAR, ...)   // Only bound a buffer of WCHAR[4] - Expecting String Data Right Truncation behavior  
Versione del driver ODBC di SQL Server Native Client Risultato della lunghezza o dell'indicatore Descrizione
SQL Server 2008 R2 (10.50.x) Native Client o versioni precedenti 20 SQLFetch segnala che è presente un troncamento sul lato destro dei dati.

La lunghezza corrisponde a quella dei dati restituiti e non di quelli archiviati (viene presupposta una conversione *2 CHAR a WCHAR che potrebbe essere errata per i glifi).

I dati archiviati nel buffer sono 123\0. Viene garantito che la terminazione del buffer sia NULL.
SQL Server 2012 (11.x) Native Client (versione 11.0.2100.60) o successiva -4 (SQL_NO_TOTAL) SQLFetch segnala che è presente un troncamento sul lato destro dei dati.

Tramite la lunghezza viene indicato -4 (SQL_NO_TOTAL) poiché il resto dei dati non è stato convertito.

I dati archiviati nel buffer sono 123\0. - Viene garantito che la terminazione del buffer sia NULL.

SQLBindParameter (comportamento del parametro OUTPUT)

Query: create procedure spTest @p1 varchar(max) OUTPUT

select @p1 = replicate('B', 1234)

SQLBindParameter(... SQL_W_CHAR, ...)   // Only bind up to first 64 characters  
Versione del driver ODBC di SQL Server Native Client Risultato della lunghezza o dell'indicatore Descrizione
SQL Server 2008 R2 (10.50.x) Native Client o versioni precedenti 2468 SQLFetch non restituisce altri dati disponibili.

SQLMoreResults restituisce non più dati disponibili.

Tramite la lunghezza vengono indicate le dimensioni dei dati restituiti dal server e non di quelli archiviati nel buffer.

Nel buffer originale sono contenuti 63 byte e un carattere di terminazione NULL. Viene garantito che la terminazione del buffer sia NULL.
SQL Server 2012 (11.x) Native Client (versione 11.0.2100.60) o successiva -4 (SQL_NO_TOTAL) SQLFetch non restituisce altri dati disponibili.

SQLMoreResults restituisce non più dati disponibili.

Tramite la lunghezza viene indicato (-4) SQL_NO_TOTAL poiché il resto dei dati non è stato convertito.

Nel buffer originale sono contenuti 63 byte e un carattere di terminazione NULL. Viene garantito che la terminazione del buffer sia NULL.

Esecuzione delle conversioni CHAR e WCHAR

Il driver ODBC di SQL Server 2012 (11.x) Native Client offre diversi modi per eseguire conversioni CHAR e WCHAR. La logica è simile alla modifica dei BLOB (varchar(max), nvarchar(max), ...):

  • I dati vengono salvati o troncati nel buffer specificato durante l'associazione con SQLBindCol o SQLBindParameter.

  • Se non si esegue l'associazione, è possibile recuperare i dati in blocchi usando SQLGetData e SQLParamData.

Vedi anche

Funzionalità di SQL Server Native Client