Verhaltensänderungen des ODBC-Treibers bei der Behandlung von Zeichenkonvertierungen

Gilt für:SQL ServerAzure SQL-DatenbankAzure SQL Managed InstanceAzure Synapse AnalyticsAnalytics Platform System (PDW)

Wichtig

Der SQL Server Native Client (häufig abgekürzt mit SNAC) wurde aus SQL Server 2022 (16.x) und SQL Server Management Studio 19 (SSMS) entfernt. Der SQL Server Native Client (SQLNCLI oder SQLNCLI11) und der Microsoft OLE DB-Legacyanbieter für SQL Server (SQLOLEDB) werden für neue Anwendungsentwicklungen nicht empfohlen. Verwenden Sie in Zukunft den neuen Microsoft OLE DB-Treiber für SQL Server (MSOLEDBSQL) oder den neuesten Microsoft ODBC Driver for SQL Server. Informationen zu SQLNCLI, das als Komponente von SQL Server Datenbank-Engine (Versionen 2012 bis 2019) ausgeliefert wird, finden Sie in dieser Supportlebenszyklus-Ausnahme.

Der SQL Server 2012 (11.x) Native Client ODBC-Treiber (SQLNCLI11.dll) hat die Funktionsweise der Konvertierungen SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) und SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)) geändert. ODBC-Funktionen, z. B. SQLGetData, SQLBindCol, SQLBindParameter, geben (-4 SQL_NO_TOTAL) als Längen-/Indikatorparameter zurück, wenn sie den ODBC-Treiber SQL Server 2012 Native Client verwenden. Frühere Versionen des SQL Server Native Client ODBC-Treibers haben einen Längenwert zurückgegeben, der falsch sein kann.

SQLGetData-Verhalten

Bei vielen Windows-Funktionen können Sie die Puffergröße 0 angeben, wobei die zurückgegebene Länge der Größe der zurückgegebenen Daten entspricht. Windows-Programmierer verwenden häufig das folgende Muster:

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  

SQLGetData sollte in diesem Szenario jedoch nicht verwendet werden. Das folgende Muster sollte nicht verwendet werden:

// 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 kann nur aufgerufen werden, um Blöcke der tatsächlichen Daten abzurufen. Die Verwendung von SQLGetData zum Abrufen der Datengröße wird nicht nicht unterstützt.

Im Folgenden wird veranschaulicht, wie sich der Treiberwechsel bei Verwendung des falschen Musters auswirkt. Diese Anwendung fragt eine varchar-Spalte und -Bindung als Unicode ab (SQL_UNICODE/SQL_WCHAR):

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

SQLGetData(hstmt, SQL_WCHAR, ....., (SQLPOINTER*) 0x1, 0 , &iSize);   // Attempting to determine storage size needed  
SQL Server Native Client ODBC-Treiberversion Längen- oder Indikatorergebnis BESCHREIBUNG
SQL Server 2008 R2 (10.50.x) Native Client oder früher 6 Der Treiber geht fälschlicherweise davon aus, dass die Konvertierung von CHAR in WCHAR als "Länge * 2" durchgeführt wird.
SQL Server 2012 (11.x) Native Client (Version 11.0.2100.60) oder höher -4 (SQL_NO_TOTAL) Der Treiber geht nicht mehr davon aus, dass die Konvertierung von CHAR in WCHAR bzw. WCHAR in CHAR eine Multiplikations- (*2) oder Divisionsaktion (/2) ist.

Das Aufrufen von SQLGetData gibt nicht mehr die Länge der erwarteten Konvertierung zurück. Der Treiber erkennt die Konvertierung in bzw. aus CHAR und WCHAR und gibt anstelle von "*2" oder "/2" das Ergebnis (-4) SQL_NO_TOTAL zurück; dieses Verhalten kann falsch sein.

Verwenden Sie SQLGetData , um die Blöcke der Daten abzurufen. (Dargestellter Pseudocode:)

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  
      }  
   }  
}  

SQLBindCol-Verhalten

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

SQLBindCol(... SQL_W_CHAR, ...)   // Only bound a buffer of WCHAR[4] - Expecting String Data Right Truncation behavior  
SQL Server Native Client ODBC-Treiberversion Längen- oder Indikatorergebnis BESCHREIBUNG
SQL Server 2008 R2 (10.50.x) Native Client oder früher 20 SQLFetch meldet, dass auf der rechten Seite der Daten ein Abschneiden vorliegt.

Die Länge entspricht der Länge der zurückgegebenen Daten und nicht der Größe der gespeicherten Daten (dabei wird die Konvertierung von CHAR in WCHAR mit Multiplikation (*2) vorausgesetzt, was für Symbole u. U. falsch ist).

Im Puffer gespeicherte Daten sind 123\0. Der Puffer ist garantiert NULL-terminiert.
SQL Server 2012 (11.x) Native Client (Version 11.0.2100.60) oder höher -4 (SQL_NO_TOTAL) SQLFetch meldet, dass auf der rechten Seite der Daten ein Abschneiden vorliegt.

Die Länge entspricht -4 (SQL_NO_TOTAL), weil die übrigen Daten nicht konvertiert wurden.

Die im Puffer gespeicherten Daten haben eine Größe von 123\0. - Der Puffer ist garantiert NULL-terminiert.

SQLBindParameter (Verhalten des OUTPUT-Parameters)

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

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

SQLBindParameter(... SQL_W_CHAR, ...)   // Only bind up to first 64 characters  
SQL Server Native Client ODBC-Treiberversion Längen- oder Indikatorergebnis BESCHREIBUNG
SQL Server 2008 R2 (10.50.x) Native Client oder früher 2.468 SQLFetch gibt keine weiteren verfügbaren Daten zurück.

SQLMoreResults gibt keine weiteren verfügbaren Daten zurück.

Die Länge gibt die Größe der vom Server zurückgegebenen, nicht jedoch die Größe der im Puffer gespeicherten Daten an.

Der ursprüngliche Puffer enthält 63 Bytes und einen NULL-Terminator. Der Puffer ist garantiert NULL-terminiert.
SQL Server 2012 (11.x) Native Client (Version 11.0.2100.60) oder höher -4 (SQL_NO_TOTAL) SQLFetch gibt keine weiteren verfügbaren Daten zurück.

SQLMoreResults gibt keine weiteren verfügbaren Daten zurück.

Die Länge entspricht (-4) SQL_NO_TOTAL, weil die übrigen Daten nicht konvertiert wurden.

Der ursprüngliche Puffer enthält 63 Bytes und einen NULL-Terminator. Der Puffer ist garantiert NULL-terminiert.

Ausführen von CHAR- und WCHAR-Konvertierungen

Der SQL Server 2012 (11.x) Native Client ODBC-Treiber bietet mehrere Möglichkeiten, CHAR- und WCHAR-Konvertierungen durchzuführen. Die Logik ähnelt der Bearbeitung von Blobs (varchar(max), nvarchar(max), ...):

  • Daten werden beim Binden mit SQLBindCol oder SQLBindParameter im angegebenen Puffer gespeichert oder abgeschnitten.

  • Wenn Sie keine Bindung durchführen, können Sie die Daten mit SQLGetData und SQLParamData in Blöcken abrufen.

Weitere Informationen

SQL Server Native Client-Funktionen