Perilaku Driver ODBC Berubah Saat Menangani Konversi Karakter

Berlaku untuk:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse Analytics AnalyticsPlatform System (PDW)

Penting

SQL Server Native Client (sering disingkat SNAC) telah dihapus dari SQL Server 2022 (16.x) dan SQL Server Management Studio 19 (SSMS). SQL Server Native Client (SQLNCLI atau SQLNCLI11) dan Penyedia Microsoft OLE DB warisan untuk SQL Server (SQLOLEDB) tidak direkomendasikan untuk pengembangan aplikasi baru. Beralih ke Microsoft OLE DB Driver (MSOLEDBSQL) baru untuk SQL Server atau Microsoft ODBC Driver terbaru untuk SQL Server ke depannya. Untuk SQLNCLI yang dikirim sebagai komponen SQL Server Database Engine (versi 2012 hingga 2019), lihat pengecualian Siklus Hidup Dukungan ini.

Driver ODBC Klien Asli SQL Server 2012 (11.x) (SQLNCLI11.dll) mengubah cara SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) dan konversi SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX).. Fungsi ODBC, seperti SQLGetData, SQLBindCol, SQLBindParameter, mengembalikan (-4) SQL_NO_TOTAL sebagai parameter panjang/indikator saat menggunakan driver ODBC Klien Asli SQL Server 2012. Versi sebelumnya dari driver ODBC SQL Server Native Client mengembalikan nilai panjang, yang dapat salah.

Perilaku SQLGetData

Banyak fungsi Windows memungkinkan Anda menentukan ukuran buffer 0 dan panjang yang dikembalikan adalah ukuran data yang dikembalikan. Pola berikut umum untuk programmer 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  

Namun, SQLGetData tidak boleh digunakan dalam skenario ini. Pola berikut tidak boleh digunakan:

// 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 hanya dapat dipanggil untuk mengambil potongan data aktual. Menggunakan SQLGetData untuk mendapatkan ukuran data tidak didukung.

Berikut ini menunjukkan dampak perubahan driver saat Anda menggunakan pola yang salah. Aplikasi ini mengkueri kolom varchar dan pengikatan sebagai Unicode (SQL_UNICODE/SQL_WCHAR):

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

SQLGetData(hstmt, SQL_WCHAR, ....., (SQLPOINTER*) 0x1, 0 , &iSize);   // Attempting to determine storage size needed  
Versi Driver ODBC Klien Asli SQL Server Panjang atau Hasil Indikator Deskripsi
Klien Asli SQL Server 2008 R2 (10.50.x) atau yang lebih lama 6 Driver salah mengasumsikan bahwa mengonversi CHAR ke WCHAR dapat dicapai dengan panjang * 2.
Klien Asli SQL Server 2012 (11.x) (versi 11.0.2100.60) atau yang lebih baru -4 (SQL_NO_TOTAL) Driver tidak lagi mengasumsikan bahwa mengonversi dari CHAR ke WCHAR atau WCHAR ke CHAR adalah tindakan (kalikan) *2 atau (bagi)/2.

Memanggil SQLGetData tidak lagi mengembalikan panjang konversi yang diharapkan. Driver mendeteksi konversi ke atau dari CHAR dan WCHAR dan mengembalikan (-4) SQL_NO_TOTAL alih-alih perilaku *2 atau /2 yang bisa salah.

Gunakan SQLGetData untuk mengambil potongan data. (Kode semu ditampilkan:)

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

Perilaku SQLBindCol

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

SQLBindCol(... SQL_W_CHAR, ...)   // Only bound a buffer of WCHAR[4] - Expecting String Data Right Truncation behavior  
Versi Driver ODBC Klien Asli SQL Server Panjang atau Hasil Indikator Deskripsi
Klien Asli SQL Server 2008 R2 (10.50.x) atau yang lebih lama 20 SQLFetch melaporkan bahwa ada pemotongan di sisi kanan data.

Panjang adalah panjang data yang dikembalikan, bukan apa yang disimpan (mengasumsikan *2 CHAR ke konversi WCHAR yang dapat salah untuk glyph).

Data yang disimpan dalam buffer adalah 123\0. Buffer dijamin null dihentikan.
Klien Asli SQL Server 2012 (11.x) (versi 11.0.2100.60) atau yang lebih baru -4 (SQL_NO_TOTAL) SQLFetch melaporkan bahwa ada pemotongan di sisi kanan data.

Panjang menunjukkan -4 (SQL_NO_TOTAL) karena data lainnya tidak dikonversi.

Data yang disimpan dalam buffer adalah 123\0. - Buffer dijamin null dihentikan.

SQLBindParameter (Perilaku Parameter OUTPUT)

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

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

SQLBindParameter(... SQL_W_CHAR, ...)   // Only bind up to first 64 characters  
Versi Driver ODBC Klien Asli SQL Server Panjang atau Hasil Indikator Deskripsi
Klien Asli SQL Server 2008 R2 (10.50.x) atau yang lebih lama 2468 SQLFetch tidak menampilkan lebih banyak data yang tersedia.

SQLMoreResults tidak menampilkan lebih banyak data yang tersedia.

Panjang menunjukkan ukuran data yang dikembalikan dari server, tidak disimpan dalam buffer.

Buffer asli berisi 63 byte dan terminator NULL. Buffer dijamin null dihentikan.
Klien Asli SQL Server 2012 (11.x) (versi 11.0.2100.60) atau yang lebih baru -4 (SQL_NO_TOTAL) SQLFetch tidak menampilkan lebih banyak data yang tersedia.

SQLMoreResults tidak menampilkan lebih banyak data yang tersedia.

Panjang menunjukkan (-4) SQL_NO_TOTAL karena data lainnya tidak dikonversi.

Buffer asli berisi 63 byte dan terminator NULL. Buffer dijamin null dihentikan.

Melakukan Konversi CHAR dan WCHAR

Driver ODBC Klien Asli SQL Server 2012 (11.x) menawarkan beberapa cara untuk melakukan konversi CHAR dan WCHAR. Logikanya mirip dengan memanipulasi blob (varchar(max), nvarchar(max), ...):

  • Data disimpan atau dipotong ke dalam buffer yang ditentukan saat mengikat dengan SQLBindCol atau SQLBindParameter.

  • Jika Anda tidak mengikat, Anda dapat mengambil data dalam gugus dengan menggunakan SQLGetData dan SQLParamData.

Lihat Juga

Fitur Klien Asli SQL Server