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

Die Konvertierung der Datentypen SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) und SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)) hat sich beim SQL Server 2012 Native Client-ODBC-Treiber (SQLNCLI11.dll) geändert. ODBC-Funktionen wie SQLGetData, SQLBindCol und SQLBindParameter geben bei Verwendung des SQL Server 2012 Native Client-ODBC-Treibers (-4) SQL_NO_TOTAL als Längen-/Indikatorparameter zurück. Von früheren Versionen des SQL Server Native Client-ODBC-Treibers wurde ein Längenwert zurückgegeben, was möglicherweise falsch ist.

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 Segmente tatsächlicher Daten abzurufen. Die Verwendung von SQLGetData zum Abrufen der Datengröße wird nicht unterstützt.

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

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

Durch Aufrufen von SQLGetData wird nicht mehr die Länge der erwarteten Konvertierung zurückgegeben. 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 Datensegmente 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 Native Client oder früher

20

  • SQLFetch meldet, dass die Daten auf der rechten Seite abgeschnitten sind.

  • 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).

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

SQL Server 2012 Native Client (Version 11.0.2100.60) oder höher

-4 (SQL_NO_TOTAL)

  • SQLFetch meldet, dass die Daten auf der rechten Seite abgeschnitten sind.

  • 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 Native Client oder früher

2468

  • SQLFetch gibt zurück, dass keine weiteren Daten verfügbar sind.

  • SQLMoreResults gibt zurück, dass keine weiteren Daten verfügbar sind.

  • 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 Native Client (Version 11.0.2100.60) oder höher

-4 (SQL_NO_TOTAL)

  • SQLFetch gibt zurück, dass keine weiteren Daten verfügbar sind.

  • SQLMoreResults gibt zurück, dass keine weiteren Daten verfügbar sind.

  • 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 Native Client-ODBC-Treiber bietet verschiedene Möglichkeiten zum Durchführen von CHAR- und WCHAR-Konvertierungen. Die Logik ist vergleichbar mit dem Bearbeiten von BLOBs (varchar(max), nvarchar(max), …):

  • Die Daten werden bei der Bindung mit SQLBindCol oder SQLBindParameter im angegebenen Puffer gespeichert oder gekürzt.

  • Wenn Sie keine Bindung durchführen, können Sie die Daten durch Verwendung von SQLGetData und SQLParamData in Segmenten abrufen.

Siehe auch

Andere Ressourcen

SQL Server Native Client-Funktionen