--- title: "ODBC change handling char conversions" ms.custom: "" ms.date: "03/14/2017" ms.prod: sql ms.reviewer: "" ms.technology: native-client ms.topic: "reference" ms.assetid: 682a232a-bf89-4849-88a1-95b2fbac1467 author: markingmyname ms.author: maghan monikerRange: ">=aps-pdw-2016||=azuresqldb-current||=azure-sqldw-latest||>=sql-server-2016||=sqlallproducts-allversions||>=sql-server-linux-2017||=azuresqldb-mi-current" --- # ODBC Driver Behavior Change When Handling Character Conversions [!INCLUDE[appliesto-ss-asdb-asdw-pdw-md](../../../includes/appliesto-ss-asdb-asdw-pdw-md.md)] The [!INCLUDE[ssSQL11](../../../includes/sssql11-md.md)] Native Client ODBC Driver (SQLNCLI11.dll) changed how it does of SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) and SQL_CHAR\* (CHAR/VARCHAR/NARCHAR(MAX)) conversions. ODBC functions, such as SQLGetData, SQLBindCol, SQLBindParameter, return (-4) SQL_NO_TOTAL as the length/indicator parameter when using the [!INCLUDE[ssNoVersion](../../../includes/ssnoversion-md.md)] 2012 Native Client ODBC driver. Prior versions of the [!INCLUDE[ssNoVersion](../../../includes/ssnoversion-md.md)] Native Client ODBC driver returned a length value, which can be incorrect. ## SQLGetData Behavior Many Windows functions let you specify a buffer size of 0 and the returned length is the size of the returned data. The following pattern is common for Windows programmers: ``` 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 ``` However, **SQLGetData** should not be used in this scenario. The following pattern should not be used: ``` // 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** can only be called to retrieve chunks of actual data. Using **SQLGetData** to get the size of data is not unsupported. The following shows the impact of the driver change when you use the incorrect pattern. This application queries a **varchar** column and binding as 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 ``` |[!INCLUDE[ssNoVersion](../../../includes/ssnoversion-md.md)] Native Client ODBC Driver version|Length or Indicator Outcome|Description| |-----------------------------------------------------------------|---------------------------------|-----------------| |[!INCLUDE[ssKilimanjaro](../../../includes/sskilimanjaro-md.md)] Native Client or earlier|6|The driver incorrectly assumed that converting CHAR to WCHAR could be accomplished as length * 2.| |[!INCLUDE[ssSQL11](../../../includes/sssql11-md.md)] Native Client (version 11.0.2100.60) or later|-4 (SQL_NO_TOTAL)|The driver no longer assumes that converting from CHAR to WCHAR or WCHAR to CHAR is a (multiply) \*2 or (divide)/2 action.

Calling **SQLGetData** no longer returns the length of the expected conversion. The driver detects the conversion to or from CHAR and WCHAR and returns (-4) SQL_NO_TOTAL instead of *2 or /2 behavior that could be incorrect.| Use **SQLGetData** to retrieve the chunks of the data. (Pseudo code shown:) ``` 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 Behavior Query: `select convert(varchar(36), '1234567890')` ``` SQLBindCol(... SQL_W_CHAR, ...) // Only bound a buffer of WCHAR[4] - Expecting String Data Right Truncation behavior ``` |[!INCLUDE[ssNoVersion](../../../includes/ssnoversion-md.md)] Native Client ODBC Driver version|Length or Indicator Outcome|Description| |-----------------------------------------------------------------|---------------------------------|-----------------| |[!INCLUDE[ssKilimanjaro](../../../includes/sskilimanjaro-md.md)] Native Client or earlier|20|**SQLFetch** reports that there is a truncation on the right side of the data.

Length is the length of the data returned, not what was stored (assumes *2 CHAR to WCHAR conversion which can be incorrect for glyphs).

Data stored in buffer is 123\0. Buffer is guaranteed to be NULL terminated.| |[!INCLUDE[ssSQL11](../../../includes/sssql11-md.md)] Native Client (version 11.0.2100.60) or later|-4 (SQL_NO_TOTAL)|**SQLFetch** reports that there is a truncation on the right side of the data.

Length indicates -4 (SQL_NO_TOTAL) because the rest of the data was not converted.

Data stored in the buffer is 123\0. - Buffer is guaranteed to be NULL terminated.| ## SQLBindParameter (OUTPUT Parameter Behavior) Query: `create procedure spTest @p1 varchar(max) OUTPUT` `select @p1 = replicate('B', 1234)` ``` SQLBindParameter(... SQL_W_CHAR, ...) // Only bind up to first 64 characters ``` |[!INCLUDE[ssNoVersion](../../../includes/ssnoversion-md.md)] Native Client ODBC Driver version|Length or Indicator Outcome|Description| |-----------------------------------------------------------------|---------------------------------|-----------------| |[!INCLUDE[ssKilimanjaro](../../../includes/sskilimanjaro-md.md)] Native Client or earlier|2468|**SQLFetch** returns no more data available.

**SQLMoreResults** returns no more data available.

Length indicates the size of the data returned from server, not stored in buffer.

Original buffer contains 63 bytes and a NULL terminator. Buffer is guaranteed to be NULL terminated.| |[!INCLUDE[ssSQL11](../../../includes/sssql11-md.md)] Native Client (version 11.0.2100.60) or later|-4 (SQL_NO_TOTAL)|**SQLFetch** returns no more data available.

**SQLMoreResults** returns no more data available.

Length indicates (-4) SQL_NO_TOTAL because the rest of the data was not converted.

Original buffer contains 63 bytes and a NULL terminator. Buffer is guaranteed to be NULL terminated.| ## Performing CHAR and WCHAR Conversions The [!INCLUDE[ssSQL11](../../../includes/sssql11-md.md)] Native Client ODBC driver offers several ways to perform CHAR and WCHAR conversions. The logic is similar to manipulating blobs (varchar(max), nvarchar(max), ...): - Data is saved or truncated into the specified buffer when binding with **SQLBindCol** or **SQLBindParameter**. - If you do not bind, you can retrieve the data in chunks by using **SQLGetData** and **SQLParamData**. ## See Also [SQL Server Native Client Features](../../../relational-databases/native-client/features/sql-server-native-client-features.md)