使用大數值類型
在 SQL Server 2005 之前,使用大數值資料類型時需要進行特殊處理。 大數值資料類型是指那些最大資料列大小超過 8 KB 的資料類型。 SQL Server 2005 推出了 max 規範,這個規範可用於 varchar、nvarchar 和 varbinary 資料類型,以允許儲存最大可達 2^31 -1 個位元組的值。 資料表資料行和 Transact-SQL 變數可以指定 varchar(max)、nvarchar(max) 或 varbinary(max) 資料類型。
[!附註]
大數值資料類型的最大大小可介於 1 和 8 KB 之間,或者也可以指定為無限制。
先前,只有 text、ntext 和 image 之類的 SQL Server 資料類型可以達到此種長度。 varchar、nvarchar 和 varbinary 的 max 規範使這些資料類型變得多餘。 不過,因為 long 資料類型仍可使用,所以大部分 OLE DB 和 ODBC 資料存取元件的介面都仍保持原樣。 為了與先前的版本保持回溯相容性,SQL Server Native Client OLE DB 提供者中的 DBCOLUMNFLAGS_ISLONG 旗標以及 SQL Server Native Client ODBC 驅動程式中的 SQL_LONGVARCHAR 仍會繼續使用。 當新類型設定為無限制的最大長度時,針對 SQL Server 2005 和更新版本所撰寫的提供者和驅動程式仍會為其繼續使用這些詞彙。
[!附註]
您也可以將 varchar(max)、nvarchar(max) 和 varbinary(max) 資料類型指定為預存程序的輸入和輸出參數類型、函數傳回類型,或者指定於 CAST 和 CONVERT 函數。
[!附註]
如果複寫資料,您可能需要將 max text repl size 伺服器組態選項設定為 -1。
SQL Server Native Client OLE DB 提供者
SQL Server Native Client OLE DB 提供者會將 varchar(max)、varbinary(max) 和 nvarchar(max) 類型分別公開為 DBTYPE_STR、DBTYPE_BYTES 和 DBTYPE_WSTR。
在 max 大小設定為無限制的資料行中,varchar(max)、varbinary(max) 和 nvarchar(max) 資料類型會在傳回資料行資料類型的核心 OLE DB 結構描述資料列集和介面中表示為 ISLONG。
命令物件的 IAccessor 實作已變更為允許以 DBTYPE_IUNKNOWN 的形式進行繫結。 如果取用者指定 DBTYPE_IUNKNOWN 並將 pObject 設定為 null,則提供者會將 ISequentialStream 介面傳回給取用者,讓取用者可以將 varchar(max)、nvarchar(max) 或 varbinary(max) 用資料流的形式傳出輸出變數。
以資料流傳輸的輸出參數值會在任何結果資料列之後傳回。 如果應用程式藉由呼叫 IMultipleResults::GetResult (而不取用所有的傳回輸出參數值) 嘗試繼續前往下一個結果集,就會傳回 DB_E_OBJECTOPEN。
為了支援資料流作業,SQL Server Native Client OLE DB 提供者需要循序存取可變長度的參數。 這表示每當 varchar(max)、nvarchchar(max) 或 varbinary(max) 資料行或輸出參數繫結至 DBTYPE_IUNKNOWN 時,DBPROP_ACCESSORDER 就必須設定為 DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS 或 DBPROPVAL_AO_SEQUENTIAL。 如果未遵守這項存取順序的限制,則對 IRowset::GetData 的呼叫會失敗,且傳回 DBSTATUS_E_UNAVAILABLE。 當沒有任何使用 DBTYPE_IUNKNOWN 的輸出繫結時,這項限制就不適用。
SQL Server Native Client OLE DB 提供者也支援將輸出參數針對大數值資料類型而繫結為 DBTYPE_IUNKNOWN,這在預存程序要將大數值類型以傳回值 (對用戶端公開為 DBTYPE_IUNKNOWN) 傳回時很有用。
為了使用這些類型,應用程式具有下列選項:
繫結為具有資料行基底類型所支援繫結的類型 (例如,對於 nvarchar(max),繫結為可繫結到 nvarchar 的類型)。 如果緩衝區不夠大,就會進行截斷,這與基底類型完全相同,只是現在使用的是較大數值。
繫結為具有資料行基底類型所支援轉換的類型,並同時指定 DBTYPE_BYREF。
繫結為 DBTYPE_IUNKNOWN 並使用資料流。
在報告資料行的最大大小時,SQL Server Native Client OLE DB 提供者會報告:
定義的最大大小,例如 varchar(2000) 資料行的最大大小為 2000,或者
"unlimited" 這個值,如果是 varchar(max) 資料行則等於 0。 這個值會針對 DBCOLUMN_COLUMNSIZE 中繼資料屬性設定。
標準轉換規則適用於 varchar(max) 資料行,代表任何對 varchar(2000) 資料行有效的轉換也對 varchar(max) 資料行有效。 相同的規則也適用於 nvarchar(max) 和 varbinary(max) 資料行。
在擷取大數值類型時,最有效的方法是繫結為 DBTYPE_IUNKNOWN,並將資料列集屬性 DBPROP_ACCESSORDER 設定為 DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS。 這樣會導致數值直接從網路以資料流的方式傳出,而不必經過中間的緩衝作業,如下列範例所示:
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250 // To include the correct interfaces.
#include <stdio.h>
#include <tchar.h>
#include <stddef.h>
#include <iostream>
using std::cout;
using std::endl;
#include <windows.h>
#include <oledb.h>
#include "sqlncli.h"
#include <oledberr.h>
#define CHKHR_GOTO(hr, errMsg, Label) \
if (FAILED(hr)) \
{ \
cout << errMsg << endl; \
goto Label; \
}
#define MAX_COL_SIZE 8000
// ROUNDUP on all platforms pointers must be aligned properly.
#define ROUNDUP_AMOUNT 8
#define ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1))
#define ROUNDUP(size) ROUNDUP_(size, ROUNDUP_AMOUNT)
HRESULT InitializeAndEstablishConnection(IDBInitialize** ppIDBInitialize);
void UnInitializeConnection(IDBInitialize* pIDBInitialize);
HRESULT CreateAndSetCommand(IDBInitialize* pIDBInitialize, ICommandText** ppICommandText);
HRESULT ProcessResultSet(IRowset* pIRowset);
void DisplayTime()
{
SYSTEMTIME st;
GetSystemTime(&st);
cout<< st.wHour << ":" << st.wMinute << ":" << st.wSecond << "." << st.wMilliseconds << endl;
}
void main()
{
HRESULT hr;
IDBInitialize* pIDBInitialize = NULL;
ICommandText* pICommandText = NULL;
IMultipleResults* pIMultipleResults = NULL;
IRowset* pIRowset = NULL;
hr = InitializeAndEstablishConnection(&pIDBInitialize);
CHKHR_GOTO(hr, L"Failed to establish connection.", _ExitMain);
hr = CreateAndSetCommand(pIDBInitialize, &pICommandText);
CHKHR_GOTO(hr, L"Failed to set up command object.", _ExitMain);
DisplayTime();
hr = pICommandText->Execute(NULL,
IID_IMultipleResults,
NULL,
NULL,
(IUnknown **) &pIMultipleResults);
CHKHR_GOTO(hr, L"Failed to execute command.", _ExitMain);
while (1)
{
hr = pIMultipleResults->GetResult(
NULL,
DBRESULTFLAG_DEFAULT,
IID_IRowset,
NULL,
(IUnknown**)&pIRowset);
CHKHR_GOTO(hr, L"Failed to obtain a results from MR object.", _ExitMain);
if (hr == DB_S_NORESULT)
break;
if (pIRowset)
{
hr = ProcessResultSet(pIRowset);
CHKHR_GOTO(hr, L"Failed to process the current Rowset.", _ExitMain);
pIRowset->Release();
pIRowset = NULL;
}
}
DisplayTime();
_ExitMain:
if (pIRowset)
{
pIRowset->Release();
pIRowset = NULL;
}
if (pIMultipleResults)
{
pIMultipleResults->Release();
pIMultipleResults = NULL;
}
if (pICommandText)
{
pICommandText->Release();
pICommandText = NULL;
}
UnInitializeConnection(pIDBInitialize);
return;
};
HRESULT InitializeAndEstablishConnection(IDBInitialize** ppIDBInitialize)
{
HRESULT hr;
IDBInitialize* pIDBInitialize = NULL;
IDBProperties* pIDBProperties = NULL;
const int NUM_DBINIT_PROPS = 3;
const wchar_t* const g_wszServer = L".";
const wchar_t* const g_wszCatalog = L"AdventureWorks";
const wchar_t* const g_wszSecurity = L"SSPI";
DBPROPSET rgdbPropSetInit[1];
DBPROP rgdbPropInit [NUM_DBINIT_PROPS];
*ppIDBInitialize = NULL;
hr = CoInitialize(NULL);
CHKHR_GOTO(hr, L"Failed to initialize COM.", _ExitInitialize);
hr = CoCreateInstance(CLSID_SQLNCLI11,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDBInitialize,
(void**)&pIDBInitialize);
CHKHR_GOTO(hr, L"Failed to create SQLNCLI11 DataSource object.", _ExitInitialize);
for(int idxProp = 0; idxProp < NUM_DBINIT_PROPS; idxProp++)
{
VariantInit(&rgdbPropInit[idxProp].vValue);
}
rgdbPropInit[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
rgdbPropInit[0].vValue.vt = VT_BSTR;
rgdbPropInit[0].vValue.bstrVal= SysAllocString(g_wszServer);
rgdbPropInit[0].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[0].colid = DB_NULLID;
if (rgdbPropInit[0].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropInit[1].dwPropertyID = DBPROP_INIT_CATALOG;
rgdbPropInit[1].vValue.vt = VT_BSTR;
rgdbPropInit[1].vValue.bstrVal= SysAllocString(g_wszCatalog);
rgdbPropInit[1].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[1].colid = DB_NULLID;
if (rgdbPropInit[1].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropInit[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
rgdbPropInit[2].vValue.vt = VT_BSTR;
rgdbPropInit[2].vValue.bstrVal= SysAllocString(g_wszSecurity);
rgdbPropInit[2].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[2].colid = DB_NULLID;
if (rgdbPropInit[2].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropSetInit[0].guidPropertySet = DBPROPSET_DBINIT;
rgdbPropSetInit[0].cProperties = NUM_DBINIT_PROPS;
rgdbPropSetInit[0].rgProperties = rgdbPropInit;
hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
CHKHR_GOTO(hr, L"Failed to QI DataSource object for IDBProperties.", _ExitInitialize);
hr = pIDBProperties->SetProperties(1, rgdbPropSetInit);
CHKHR_GOTO(hr, L"Failed to set DataSource object Properties.", _ExitInitialize);
pIDBProperties->Release();
pIDBProperties = NULL;
hr = pIDBInitialize->Initialize();
CHKHR_GOTO(hr, L"Failed to establish connection with the server.", _ExitInitialize);
_ExitInitialize:
if (pIDBProperties)
{
pIDBProperties->Release();
pIDBProperties = NULL;
}
if (FAILED(hr))
{
if (pIDBInitialize)
{
pIDBInitialize->Release();
pIDBInitialize = NULL;
}
}
*ppIDBInitialize = pIDBInitialize;
return hr;
}
void UnInitializeConnection(IDBInitialize* pIDBInitialize)
{
if (pIDBInitialize)
{
pIDBInitialize->Uninitialize();
pIDBInitialize->Release();
pIDBInitialize = NULL;
}
CoUninitialize();
}
HRESULT CreateAndSetCommand(IDBInitialize* pIDBInitialize, ICommandText** ppICommandText)
{
HRESULT hr;
IDBCreateSession* pIDBCreateSession = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;
ICommandProperties* pICommandProperties = NULL;
DBPROPSET rgCmdPropSet[1];
DBPROP rgCmdProperties[1];
const wchar_t* const g_wCmdString = L"declare @x xml, @y nvarchar(max); select @x = (SELECT * FROM Sales.SalesOrderHeader FOR XML AUTO); select @x;";
*ppICommandText = NULL;
if (!pIDBInitialize)
{
hr = E_FAIL;
goto _ExitCreateAndSetCommand;
}
hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**) &pIDBCreateSession);
CHKHR_GOTO(hr, L"Failed to obtain IDBCreateSession interface from DSO.", _ExitCreateAndSetCommand);
hr = pIDBCreateSession->CreateSession(
NULL,
IID_IDBCreateCommand,
(IUnknown**) &pIDBCreateCommand);
CHKHR_GOTO(hr, L"Failed to Create a Session for command execution.", _ExitCreateAndSetCommand);
hr = pIDBCreateCommand->CreateCommand(
NULL,
IID_ICommandText,
(IUnknown**)&pICommandText);
CHKHR_GOTO(hr, L"Failed to Create a Command object.", _ExitCreateAndSetCommand);
hr = pICommandText->SetCommandText(DBGUID_DBSQL, g_wCmdString);
CHKHR_GOTO(hr, L"Failed to Set Command Text.", _ExitCreateAndSetCommand);
hr = pICommandText->QueryInterface(IID_ICommandProperties, (void**) &pICommandProperties);
CHKHR_GOTO(hr, L"Failed to obtain ICommandProperties interface from the command object.", _ExitCreateAndSetCommand);
rgCmdProperties[0].dwPropertyID = DBPROP_ACCESSORDER;
rgCmdProperties[0].vValue.vt = VT_I4;
rgCmdProperties[0].vValue.lVal = DBPROPVAL_AO_SEQUENTIAL;
rgCmdProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
rgCmdProperties[0].colid = DB_NULLID;
rgCmdPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
rgCmdPropSet[0].cProperties = 1;
rgCmdPropSet[0].rgProperties = rgCmdProperties;
hr = pICommandProperties->SetProperties(1, rgCmdPropSet);
CHKHR_GOTO(hr, L"Failed to Set Command object Properties.", _ExitCreateAndSetCommand);
_ExitCreateAndSetCommand:
if (pICommandProperties)
{
pICommandProperties->Release();
pICommandProperties = NULL;
}
if (pIDBCreateCommand)
{
pIDBCreateCommand->Release();
pIDBCreateCommand = NULL;
}
if (pIDBCreateSession)
{
pIDBCreateSession->Release();
pIDBCreateSession = NULL;
}
if (FAILED(hr))
{
if (pICommandText)
{
pICommandText->Release();
pICommandText = NULL;
}
}
*ppICommandText = pICommandText;
return hr;
}
HRESULT ProcessResultSet(IRowset* pIRowset)
{
HRESULT hr;
IColumnsInfo* pIColumnsInfo = NULL;
DBCOLUMNINFO* pDBColumnInfo = NULL;
ULONG lNumCols = 0;
wchar_t* pStringsBuffer = NULL;
DBBINDING* pBindings = NULL;
DBOBJECT dbobj;
ULONG idxBinding;
IAccessor* pIAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
HROW hRows[1] = {DB_NULL_HROW};
HROW* pRow = &hRows[0];
BYTE* pBuffer = NULL;
ULONG lNumRowsRetrieved;
DBLENGTH dwOffset = 0;
hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void **)&pIColumnsInfo);
CHKHR_GOTO(hr, L"Failed to QI Rowset for IColumnsInfo.", _ExitProcessResultSet);
hr = pIColumnsInfo->GetColumnInfo(&lNumCols, &pDBColumnInfo, &pStringsBuffer);
CHKHR_GOTO(hr, L"Failed to obtain Column Information.", _ExitProcessResultSet);
pBindings = new DBBINDING[lNumCols];
if (!pBindings)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
memset(pBindings, 0, sizeof(DBBINDING) * lNumCols);
dbobj.dwFlags = STGM_READ;
dbobj.iid = IID_ISequentialStream;
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
pBindings[idxBinding].iOrdinal = idxBinding + 1;
pBindings[idxBinding].obStatus = dwOffset;
pBindings[idxBinding].obLength = dwOffset + sizeof(DBSTATUS);
pBindings[idxBinding].obValue = dwOffset + sizeof(DBSTATUS) + sizeof(DBLENGTH);
pBindings[idxBinding].pTypeInfo = NULL;
pBindings[idxBinding].pBindExt = NULL;
pBindings[idxBinding].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
pBindings[idxBinding].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pBindings[idxBinding].eParamIO = DBPARAMIO_NOTPARAM;
pBindings[idxBinding].bPrecision = pDBColumnInfo[idxBinding].bPrecision;
pBindings[idxBinding].bScale = pDBColumnInfo[idxBinding].bScale;
pBindings[idxBinding].cbMaxLen = 0;
pBindings[idxBinding].wType = DBTYPE_WSTR;
// Determine the maximum number of bytes required in our buffer to
// contain the Unicode string representation of the provider's native
// data type, including room for the NULL-termination character
switch( pDBColumnInfo[idxBinding].wType )
{
case DBTYPE_NULL:
case DBTYPE_EMPTY:
case DBTYPE_I1:
case DBTYPE_I2:
case DBTYPE_I4:
case DBTYPE_UI1:
case DBTYPE_UI2:
case DBTYPE_UI4:
case DBTYPE_R4:
case DBTYPE_BOOL:
case DBTYPE_I8:
case DBTYPE_UI8:
case DBTYPE_R8:
case DBTYPE_CY:
case DBTYPE_ERROR:
// When the above types are converted to a string, they
// will all fit into 25 characters, so use that plus space
// for the NULL-terminator.
pBindings[idxBinding].cbMaxLen = (25 + 1) * sizeof(WCHAR);
break;
case DBTYPE_DECIMAL:
case DBTYPE_NUMERIC:
case DBTYPE_DATE:
case DBTYPE_DBDATE:
case DBTYPE_DBTIMESTAMP:
case DBTYPE_GUID:
// Converted to a string, the above types will all fit into
// 50 characters, so use that plus space for the terminator.
pBindings[idxBinding].cbMaxLen = (50 + 1) * sizeof(WCHAR);
break;
case DBTYPE_BYTES:
// In converting DBTYPE_BYTES to a string, each byte
// becomes two characters (e.g. 0xFF -> "FF"), so we
// will use double the maximum size of the column plus
// include space for the NULL-terminator.
pBindings[idxBinding].cbMaxLen = (pDBColumnInfo[idxBinding].ulColumnSize * 2 + 1) * sizeof(WCHAR);
break;
case DBTYPE_STR:
case DBTYPE_WSTR:
case DBTYPE_BSTR:
// Going from a string to our string representation,
// we can just take the maximum size of the column,
// a count of characters, and include space for the
// terminator, which is not included in the column size.
pBindings[idxBinding].cbMaxLen = (pDBColumnInfo[idxBinding].ulColumnSize + 1) * sizeof(WCHAR);
break;
default:
// For any other type, we will simply use our maximum
// column buffer size, since the display size of these
// columns may be variable (e.g. DBTYPE_VARIANT) or
// unknown (e.g. provider-specific types).
pBindings[idxBinding].cbMaxLen = MAX_COL_SIZE;
break;
}
// If the provider's native data type for this column is
// DBTYPE_IUNKNOWN or this is a BLOB column and the user
// has requested that we bind BLOB columns as ISequentialStream
// objects, bind this column as an ISequentialStream object if
// the provider supports our creating another ISequentialStream
// binding.
if(pDBColumnInfo[idxBinding].dwFlags & DBCOLUMNFLAGS_ISLONG)
{
pBindings[idxBinding].wType = DBTYPE_IUNKNOWN;
pBindings[idxBinding].cbMaxLen = sizeof(ISequentialStream*);
pBindings[idxBinding].pObject = (DBOBJECT *)CoTaskMemAlloc(sizeof(DBOBJECT));
if (!pBindings[idxBinding].pObject)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
// Direct the provider to create an ISequentialStream
// object over the data for this column.
pBindings[idxBinding].pObject->iid = IID_ISequentialStream;
// We want read access on the ISequentialStream
// object that the provider will create for us
pBindings[idxBinding].pObject->dwFlags = STGM_READ;
}
// Ensure that the bound maximum length is no more than the
// maximum column size in bytes that we've defined.
pBindings[idxBinding].cbMaxLen = min(pBindings[idxBinding].cbMaxLen, MAX_COL_SIZE);
// Update the offset past the end of this column's data, so
// that the next column will begin in the correct place in
// the buffer.
dwOffset = pBindings[idxBinding].cbMaxLen + pBindings[idxBinding].obValue;
// Ensure that the data for the next column will be correctly
// aligned for all platforms, or, if we're done with columns,
// that if we allocate space for multiple rows that the data
// for every row is correctly aligned.
dwOffset = ROUNDUP(dwOffset);
}
hr = pIRowset->QueryInterface(IID_IAccessor, (void **) &pIAccessor);
CHKHR_GOTO(hr, L"Failed to obtain Accessor interface", _ExitProcessResultSet);
hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA,
lNumCols,
pBindings,
0,
&hAccessor,
NULL);
CHKHR_GOTO(hr, L"Failed to create Accessor", _ExitProcessResultSet);
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
cout << pDBColumnInfo[idxBinding].pwszName << endl;
}
lNumRowsRetrieved = 0;
hr = pIRowset->GetNextRows(
NULL,
0,
1,
&lNumRowsRetrieved,
&pRow);
CHKHR_GOTO(hr, L"Failed to fetch a row from the rowset", _ExitProcessResultSet);
pBuffer = new BYTE[sizeof(DBSTATUS) + sizeof(DBLENGTH) + sizeof(IUnknown*)];
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
while(lNumRowsRetrieved && hr != DB_S_ENDOFROWSET)
{
memset(pBuffer, 0, sizeof(DBSTATUS) + sizeof(DBLENGTH) + sizeof(IUnknown*));
hr = pIRowset->GetData(hRows[0], hAccessor, pBuffer);
CHKHR_GOTO(hr, L"Failed to obtain row data", _ExitProcessResultSet);
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
if (pBindings[idxBinding].wType == DBTYPE_IUNKNOWN)
{
BYTE pbBuff[3000];
ULONG cbNeeded = sizeof(pbBuff)/sizeof(BYTE);
ULONG cbRead;
ULONG cbReadTotal = 0;
ISequentialStream* pISequentialStream = NULL;
IUnknown* pIUnknown = *((IUnknown**)(pBuffer + pBindings[idxBinding].obValue));
pIUnknown->QueryInterface(IID_ISequentialStream, (void**)&pISequentialStream);
do
{
hr = pISequentialStream->Read(pbBuff, cbNeeded, &cbRead);
cbReadTotal += cbRead;
}
while (SUCCEEDED(hr) && hr != S_FALSE && cbRead == cbNeeded);
cout << "Total Bytes Read: " << cbReadTotal << endl;
pISequentialStream->Release();
pISequentialStream = NULL;
pIUnknown->Release();
pIUnknown = NULL;
}
}
pIRowset->ReleaseRows(1, pRow, NULL, NULL, NULL);
hr = pIRowset->GetNextRows(NULL,
0,
1,
&lNumRowsRetrieved,
&pRow);
CHKHR_GOTO(hr, L"Failed to fetch a row from the rowset.", _ExitProcessResultSet);
}
_ExitProcessResultSet:
pIRowset->ReleaseRows(1, pRow, NULL, NULL, NULL);
delete [] pBuffer;
if (pIAccessor)
{
if (hAccessor != DB_NULL_HACCESSOR)
{
pIAccessor->ReleaseAccessor(hAccessor, NULL);
}
pIAccessor->Release();
pIAccessor = NULL;
}
if (pBindings)
{
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
if (pBindings[idxBinding].pObject)
CoTaskMemFree(pBindings[idxBinding].pObject);
}
}
delete [] pBindings;
CoTaskMemFree(pDBColumnInfo);
CoTaskMemFree(pStringsBuffer);
if (pIColumnsInfo)
{
pIColumnsInfo->Release();
pIColumnsInfo = NULL;
}
return hr;
}
如需有關 SQL Server Native Client OLE DB 提供者如何公開大數值資料類型的詳細資訊,請參閱<BLOB 與 OLE 物件>。
SQL Server Native Client ODBC 驅動程式
SQL Server Native Client ODBC 驅動程式會在接受或傳回 ODBC SQL 資料類型的 ODBC API 函數中,將 varchar(max)、varbinary(max) 和 nvarchar(max) 類型公開為 SQL_VARCHAR、SQL_VARBINARY 和 SQL_WVARCHAR。
在報告資料行的最大大小時,驅動程式會報告下列其中一項:
定義的最大大小,例如 varchar(2000) 資料行的最大大小為 2000,或者
"unlimited" 這個值,如果是 varchar(max) 資料行則等於 0。
標準轉換規則適用於 varchar(max) 資料行,代表任何對 varchar(2000) 資料行有效的轉換也對 varchar(max) 資料行有效。 相同的規則也適用於 nvarchar(max) 和 varbinary(max) 資料行。
下列是 ODBC API 函數的清單,這些函數經過改良,可用於大數值資料類型: