Modifica del comportamento del driver ODBC quando si gestiscono le conversioni di caratteri
Nel driver ODBC di SQL Server 2012 Native Client (SQLNCLI11.dll) è stata modificata la modalità di esecuzione delle conversioni SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) e SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Tramite le funzioni ODBC, ad esempio SQLGetData, SQLBindCol, SQLBindParameter viene restituito (-4) SQL_NO_TOTAL come parametro di lunghezza/indicatore quando si utilizza il driver ODBC di SQL Server 2012 Native Client. Dalle versioni precedenti del driver ODBC di SQL Server Native Client viene restituito un valore di lunghezza che può essere errato.
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, la funzione SQLGetData non deve essere utilizzata 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
La funzione SQLGetData può essere chiamata solo per recuperare blocchi di dati effettivi. L'utilizzo della funzione 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. Tramite questa applicazione viene eseguita una query su una colonna varchar e su 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 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 Native Client (versione 11.0.2100.60) o successive |
-4 (SQL_NO_TOTAL) |
Tramite il driver non viene più presupposto che la conversione da CHAR a WCHAR o da WCHAR a CHAR sia un'azione di (moltiplicazione) *2 o (divisione)/2. Tramite la chiamata alla funzione SQLGetData non viene più restituita 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. |
Utilizzare la funzione SQLGetData per recuperare i blocchi di 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 Native Client o versioni precedenti |
20 |
|
SQL Server 2012 Native Client (versione 11.0.2100.60) o successive |
-4 (SQL_NO_TOTAL) |
|
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 Native Client o versioni precedenti |
2468 |
|
SQL Server 2012 Native Client (versione 11.0.2100.60) o successive |
-4 (SQL_NO_TOTAL) |
|
Esecuzione delle conversioni CHAR e WCHAR
Nel driver ODBC di SQL Server 2012 Native Client sono disponibili diverse modalità di esecuzione delle conversioni CHAR e WCHAR. La logica è simile alla modifica di BLOB (varchar(max), nvarchar(max), …):
I dati vengono salvati o troncati nel buffer specificato quando si esegue l'associazione con SQLBindCol o SQLBindParameter.
Se non viene eseguita l'associazione, è possibile recuperare i dati in blocchi utilizzando SQLGetData e SQLParamData.