Modifica del comportamento del driver ODBC quando si gestiscono le conversioni di caratteri
Si applica a: SQL Server database SQL di Azure Istanza gestita di SQL di Azure Azure Synapse Analytics Piattaforma di strumenti analitici (PDW)
Importante
SQL Server Native Client (SNAC) non viene fornito con:
- SQL Server 2022 (16.x) e versioni successive
- SQL Server Management Studio 19 e versioni successive
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.
Per i nuovi progetti, usare uno dei driver seguenti:
Per SQLNCLI fornito come componente del motore di database di SQL Server (versioni dal 2012 al 2019), vedere questa Eccezione relativa al ciclo di vita del supporto.
Sql Server 2012 (11.x) Native Client ODBC Driver (SQLNCLI11.dll) ha modificato le modalità di SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) e SQL_CHAR* (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.