Eseguire una copia bulk di dati mediante IRowsetFastLoad (OLE DB)

In questo esempio viene illustrato l'utilizzo di IRowsetFastLoad per la copia bulk di record in una tabella.

Tramite il consumer viene inviata una notifica a SQLOLEDB della necessità di eseguire una copia bulk impostando su VARIANT_TRUE la proprietà SSPROP_ENABLEFASTLOAD specifica del provider SQLOLEDB. Con la proprietà impostata sull'origine dati, il consumer crea una sessione di SQLOLEDB. La nuova sessione consente al consumer di accedere a IRowsetFastLoad.

In un esempio completo viene illustrato l'utilizzo di IRowsetFastLoad per eseguire una copia bulk dei record in una tabella. In questo esempio vengono aggiunti 10 record alla tabella IRFLTable. È necessario creare la tabella IRFLTable nel database.

Per l'esempio è necessario il database di esempio AdventureWorks, che è possibile scaricare dalla home page del sito relativo a progetti della community ed esempi per Microsoft SQL Server.

Nota sulla sicurezzaNota sulla sicurezza

Se possibile, utilizzare l'autenticazione di Windows. Se l'autenticazione di Windows non è disponibile, agli utenti verrà richiesto di immettere le credenziali in fase di esecuzione. Evitare di archiviare le credenziali in un file. Se è necessario rendere persistenti le credenziali, è consigliabile crittografarle utilizzando l'API di crittografia Win32.

Per eseguire la copia bulk dei dati in una tabella di SQL Server

  1. Stabilire una connessione all'origine dati.

  2. Impostare su VARIANT_TRUE la proprietà SSPROP_ENABLEFASTLOAD dell'origine dati specifica del provider SQLOLEDB. Grazie a questa impostazione, la sessione appena creata consente al consumer di accedere a IRowsetFastLoad.

  3. Creare una sessione che richiede l'interfaccia IOpenRowset.

  4. Chiamare IOpenRowset::OpenRowset per aprire un set di righe che include tutte le righe della tabella (in cui devono essere copiati i dati utilizzando l'operazione di copia bulk).

  5. Effettuare le associazioni necessarie e creare una funzione di accesso utilizzando IAccessor::CreateAccessor.

  6. Configurare il buffer della memoria dal quale verranno copiati i dati nella tabella.

  7. Chiamare IRowsetFastLoad::InsertRow per eseguire la copia bulk dei dati nella tabella.

Esempio

In questo esempio vengono aggiunti 10 record alla tabella IRFLTable. È necessario creare la tabella IRFLTable nel database. Questo esempio non è supportato in IA64.

Eseguire il primo listato di codice (Transact-SQL) per creare la tabella utilizzata dall'applicazione.

Compilare il listato di codice C++ seguente con ole32.lib oleaut32.lib ed eseguirlo. In questa applicazione viene eseguita la connessione all'istanza predefinita di SQL Server nel computer in uso. In alcuni sistemi operativi Windows sarà necessario modificare (local) o (localhost) impostando il valore sul nome dell'istanza di SQL Server. Per connettersi a un'istanza denominata, modificare la stringa di connessione da L"(local)" in L"(local)\\nome", dove nome rappresenta l'istanza denominata. Per impostazione predefinita, SQL Server Express viene installato in un'istanza denominata. Verificare che nella variabile di ambiente INCLUDE sia presente la directory che contiene sqlncli.h.

Eseguire il terzo listato di codice (Transact-SQL) per eliminare la tabella utilizzata dall'applicazione.

USE AdventureWorks
GO

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'IRFLTable')
     DROP TABLE IRFLTable
GO

CREATE TABLE IRFLTable (col_vchar varchar(30))

// compile with: ole32.lib oleaut32.lib
#define DBINITCONSTANTS
#define OLEDBVER 0x0250   // to include correct interfaces

#include <oledb.h>
#include <oledberr.h>
#include <stdio.h>
#include <stddef.h>   // for offsetof
#include <sqlncli.h>

// @type UWORD    | 2 byte unsigned integer.
typedef unsigned short UWORD;

// @type SDWORD   | 4 byte signed integer.
typedef signed long SDWORD;

WCHAR g_wszTable[] = L"IRFLTable";
WCHAR g_strTestLOC[100] = L"localhost";
WCHAR g_strTestDBName[] = L"AdventureWorks";
const UWORD g_cOPTION = 4;
const UWORD MAXPROPERTIES = 5;
const ULONG DEFAULT_CBMAXLENGTH = 20;
IMalloc* g_pIMalloc = NULL;
IDBInitialize* g_pIDBInitialize = NULL;

// Given an ICommand pointer, properties, and query, a rowsetpointer is returned.
HRESULT CreateSessionCommand( DBPROPSET* rgPropertySets, ULONG ulcPropCount, CLSID clsidProv );

// Use to set properties and execute a given query.
HRESULT ExecuteQuery ( IDBCreateCommand* pIDBCreateCommand,
                       WCHAR* pwszQuery,
                       DBPROPSET* rgPropertySets,
                       ULONG ulcPropCount, 
                       LONG* pcRowsAffected,
                       IRowset** ppIRowset,
                       BOOL fSuccessOnly = TRUE );

// Use to set up options for call to IDBInitialize::Initialize.
void SetupOption ( DBPROPID PropID, WCHAR *wszVal, DBPROP * pDBProp );

// Sets fastload property on/off for session.
HRESULT SetFastLoadProperty(BOOL fSet);

// IRowsetFastLoad inserting data.
HRESULT FastLoadData();

// How to lay out each column in memory.
struct COLUMNDATA {
   DBLENGTH dwLength;   // Length of data (not space allocated).
   DWORD dwStatus;   // Status of column.
   BYTE bData[1];   // Store data here as a variant.
};

#define COLUMN_ALIGNVAL 8

#define ROUND_UP(Size, Amount)(((DWORD)(Size) + ((Amount)-1)) & ~((Amount)-1))

int main() {
   HRESULT hr = NOERROR;
   HRESULT hr2 = NOERROR;

   // OLE initialized?
   BOOL fInitialized = FALSE;

   // One property set for initializing.
   DBPROPSET rgPropertySets[1];

   // Properties within above property set.
   DBPROP rgDBProperties[g_cOPTION]; 

   IDBCreateCommand* pIDBCreateCommand = NULL;
   IRowset* pIRowset = NULL;
   DBPROPSET* rgProperties = NULL;
   IAccessor* pIAccessor = NULL;

   // Basic initialization.
   if ( FAILED(CoInitialize(NULL)) )
      goto cleanup;
   else
      fInitialized = TRUE;

   hr = CoGetMalloc(MEMCTX_TASK, &g_pIMalloc);
   if ((!g_pIMalloc) || FAILED(hr))
      goto cleanup;

   // Set up property set for call to IDBInitialize in CreateSessionCommand.
   rgPropertySets[0].rgProperties = rgDBProperties;
   rgPropertySets[0].cProperties = g_cOPTION;
   rgPropertySets[0].guidPropertySet = DBPROPSET_DBINIT;

   SetupOption(DBPROP_INIT_CATALOG, g_strTestDBName, &rgDBProperties[0]);

   SetupOption(DBPROP_AUTH_INTEGRATED, L"SSPI", &rgDBProperties[1]);

   SetupOption(DBPROP_INIT_DATASOURCE, g_strTestLOC, &rgDBProperties[3]);

   if (!SUCCEEDED( hr = CreateSessionCommand(rgPropertySets, 1, CLSID_SQLNCLI11)))
      goto cleanup;

   // Get IRowsetFastLoad and insert data into IRFLTable.
   if (FAILED(hr = FastLoadData()))
      goto cleanup;

cleanup:
   // Release memory.
   if (rgProperties && rgProperties->rgProperties)
      delete [](rgProperties->rgProperties);
   if (rgProperties)
      delete []rgProperties;
   if (pIDBCreateCommand)
      pIDBCreateCommand->Release();

   if (pIAccessor)
      pIAccessor->Release();

   if (pIRowset)
      pIRowset->Release();
   if (g_pIMalloc)
      g_pIMalloc->Release();

   if (g_pIDBInitialize) {    
      hr2 = g_pIDBInitialize->Uninitialize();
      if (FAILED(hr2))
         printf("Uninitialize failed\n");
   }

   if (fInitialized)
      CoUninitialize();

   if (SUCCEEDED(hr))
      printf("Test completed successfully.\n\n");
   else
      printf("Test failed.\n\n");
}

HRESULT FastLoadData() {
   HRESULT hr = E_FAIL;
   HRESULT hr2 = E_FAIL;
   DBID TableID;

   IDBCreateSession* pIDBCreateSession = NULL;
   IOpenRowset* pIOpenRowsetFL = NULL;
   IRowsetFastLoad* pIFastLoad = NULL;

   IAccessor* pIAccessor = NULL;
   HACCESSOR hAccessor = 0;
   DBBINDSTATUS oneStatus = 0;

   DBBINDING oneBinding;
   ULONG ulOffset = 0;
   TableID.uName.pwszName = NULL;
   LONG i = 0;
   void* pData = NULL;
   COLUMNDATA* pcolData = NULL;

   TableID.eKind = DBKIND_NAME;
   // if ( !(TableID.uName.pwszName = new WCHAR[wcslen(g_wszTable) + 2]) )
   LPOLESTR x = TableID.uName.pwszName = new WCHAR[wcslen(g_wszTable) + 2];
   if (!x)
      return E_FAIL;
   wcsncpy_s(TableID.uName.pwszName, wcslen(g_wszTable) + 2, g_wszTable, wcslen(g_wszTable)+1);
   TableID.uName.pwszName[wcslen(g_wszTable)+1] = (WCHAR) NULL;

   // Get the fastload pointer.
   if (FAILED(hr = SetFastLoadProperty(TRUE)))
      goto cleanup;

   if ( FAILED( hr = 
      g_pIDBInitialize->QueryInterface( IID_IDBCreateSession, (void **) &pIDBCreateSession )))
      goto cleanup;

   if ( FAILED( hr = 
      pIDBCreateSession->CreateSession( NULL, IID_IOpenRowset, (IUnknown **) &pIOpenRowsetFL )))
      goto cleanup;

   // Get IRowsetFastLoad initialized to use the test table.
   if (FAILED(hr = 
      pIOpenRowsetFL->OpenRowset(NULL, 
                                 &TableID, 
                                 NULL, 
                                 IID_IRowsetFastLoad, 
                                 0, 
                                 NULL, 
                                 (LPUNKNOWN *)&pIFastLoad)))
      goto cleanup;

   // Set up custom bindings.
   oneBinding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
   oneBinding.iOrdinal = 1;
   oneBinding.pTypeInfo = NULL;
   oneBinding.obValue = ulOffset + offsetof(COLUMNDATA,bData);
   oneBinding.obLength = ulOffset + offsetof(COLUMNDATA,dwLength);
   oneBinding.obStatus = ulOffset + offsetof(COLUMNDATA,dwStatus);
   oneBinding.cbMaxLen = 30;   // Size of varchar column.
   oneBinding.pTypeInfo = NULL;
   oneBinding.pObject = NULL;
   oneBinding.pBindExt = NULL;
   oneBinding.dwFlags = 0;
   oneBinding.eParamIO = DBPARAMIO_NOTPARAM;
   oneBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
   oneBinding.bPrecision= 0;   
   oneBinding.bScale = 0;  
   oneBinding.wType = DBTYPE_STR;  
   ulOffset = oneBinding.cbMaxLen + offsetof(COLUMNDATA, bData);
   ulOffset = ROUND_UP( ulOffset, COLUMN_ALIGNVAL );

   if ( FAILED( hr = 
      pIFastLoad->QueryInterface(IID_IAccessor, (void **) &pIAccessor))) 
      return hr;

   if (FAILED(hr = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 
                                               1, 
                                               &oneBinding, 
                                               ulOffset, 
                                               &hAccessor, 
                                               &oneStatus)))
      return hr;

   // Set up memory buffer.
   pData = new BYTE[40];
   if (!(pData /* = new BYTE[40]*/ )) {
      hr = E_FAIL;
      goto cleanup;
   }

   pcolData = (COLUMNDATA*)pData;
   pcolData->dwLength = (SDWORD)strlen("Show the data") + 1;
   pcolData->dwStatus = 0;
   memcpy(&(pcolData->bData), "Show the data", strlen("Show the data") + 1);

   for ( i = 0 ; i < 10 ; i++ )
      if (FAILED(hr = pIFastLoad->InsertRow(hAccessor, pData)))
         goto cleanup;

   if (FAILED(hr = pIFastLoad->Commit(TRUE)))
      printf("Error on IRFL::Commit\n");

cleanup:
   if (FAILED(hr2 = SetFastLoadProperty(FALSE)))
      printf("SetFastLoadProperty(FALSE) failed with %x", hr2);

   if (pIAccessor && hAccessor)
      if (FAILED(pIAccessor->ReleaseAccessor(hAccessor, NULL)))
         hr = E_FAIL;

   if (pIAccessor)
      pIAccessor->Release();

   if (pIFastLoad)
      pIFastLoad->Release();

   if (pIOpenRowsetFL)
      pIOpenRowsetFL->Release();

   if (pIDBCreateSession)
      pIDBCreateSession->Release();

   if (TableID.uName.pwszName)
      delete []TableID.uName.pwszName;

   return hr;
}

HRESULT SetFastLoadProperty(BOOL fSet) {
   HRESULT hr = S_OK;
   IDBProperties* pIDBProps = NULL;
   DBPROP rgProps[1];
   DBPROPSET PropSet;

   VariantInit(&rgProps[0].vValue);

   rgProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;
   rgProps[0].colid = DB_NULLID;
   rgProps[0].vValue.vt = VT_BOOL;
   rgProps[0].dwPropertyID = SSPROP_ENABLEFASTLOAD;

   if (fSet == TRUE)
      rgProps[0].vValue.boolVal = VARIANT_TRUE;
   else
      rgProps[0].vValue.boolVal = VARIANT_FALSE;

   PropSet.rgProperties = rgProps;
   PropSet.cProperties = 1;
   PropSet.guidPropertySet = DBPROPSET_SQLSERVERDATASOURCE;

   if (SUCCEEDED(hr = g_pIDBInitialize->QueryInterface(IID_IDBProperties, (LPVOID *)&pIDBProps)))
      hr = pIDBProps->SetProperties(1, &PropSet);

   VariantClear(&rgProps[0].vValue); 

   if (pIDBProps)
      pIDBProps->Release();

   return hr;
}

HRESULT CreateSessionCommand( DBPROPSET* rgPropertySets,// @parm [in] property sets
                              ULONG ulcPropCount,   // @parm [in] count of prop sets.
                              CLSID clsidProv) {  // @parm [in] Provider CLSID.

   HRESULT hr = NOERROR;
   IDBCreateSession* pIDBCreateSession = NULL;
   IDBProperties* pIDBProperties = NULL;
   UWORD i = 0, j = 0;   // indexes.

   if (ulcPropCount && !rgPropertySets) {
      hr = E_INVALIDARG;
      return hr;
   }

   if (!SUCCEEDED(hr = CoCreateInstance(clsidProv, 
                                        NULL,CLSCTX_INPROC_SERVER,
                                        IID_IDBInitialize,
                                        (void **)&g_pIDBInitialize)))
      goto CLEANUP;

   if (!SUCCEEDED(hr = g_pIDBInitialize->QueryInterface( IID_IDBProperties,
                                                         (void **)&pIDBProperties)))
      goto CLEANUP;

   if (!SUCCEEDED(hr = pIDBProperties->SetProperties(ulcPropCount, rgPropertySets)))
      goto CLEANUP;

   if (!SUCCEEDED(hr = g_pIDBInitialize->Initialize())) {
      printf("Call to initialize failed.\n");
      goto CLEANUP;
   }

CLEANUP:
   if (pIDBProperties)
      pIDBProperties->Release();
   if (pIDBCreateSession)
      pIDBCreateSession->Release();

   for (i = 0 ; i < ulcPropCount ; i++)
      for (j = 0 ; j < rgPropertySets[i].cProperties ; j++)
         VariantClear(&(rgPropertySets[i].rgProperties[j]).vValue);
   return hr;
}

void SetupOption (DBPROPID PropID, WCHAR *wszVal, DBPROP * pDBProp ) {
   pDBProp->dwPropertyID = PropID;
   pDBProp->dwOptions = DBPROPOPTIONS_REQUIRED;
   pDBProp->colid = DB_NULLID;
   pDBProp->vValue.vt = VT_BSTR;
   pDBProp->vValue.bstrVal = SysAllocStringLen(wszVal, wcslen(wszVal));
}

USE AdventureWorks
GO

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'IRFLTable')
     DROP TABLE IRFLTable
GO